Как добавить стили только на страницы с определённым шорткодом?
Я запускаю функцию на страницах, где используется шорткод [make-me-a-map]
. Она подключает javascript к странице и создаёт карту. Эта часть работает нормально (ура!), но у меня возникли проблемы с добавлением стилей только на эти страницы аналогичным образом.
functions.php (Текущая версия)
add_shortcode("make-me-a-map", "create_new_map");
add_action("wp_head", "style_my_new_map");
function create_new_map () {
global $new_map_called_for;
$map_called_for = true;
wp_register_script('make-a-new-map', get_template_directory_uri() . '/js/new-map.js', array('jquery'), '0.1', true);
wp_enqueue_script('make-a-new-map');
}
function style_my_new_map() {
global $new_map_called_for;
if ($new_map_called_for == true) {
$tDir = get_template_directory_uri();
// Выводим стили напрямую, потому что wp_register_style & wp_enqueue_style сейчас не работают
// И add_action('wp_enqueue_script', 'style_my_new_maps') с этими функциями добавит стили на все страницы
// А не только на те, где есть шорткод [make-me-a-map]
echo "<link rel='stylesheet' href='", $tDir, "/css/new-map.css' />", PHP_EOL;
// Выводим JavaScript напрямую на страницу (не получилось сделать через wp_enqueue_script) чтобы js-код знал путь к шаблону
echo '<script type="text/javascript">var templateDir = "', get_template_directory_uri(), '";</script>', PHP_EOL;
$map_called_for = false;
}
}
К сожалению, это не работает так, как нужно. Стили добавляются на все страницы.
Я не использовал wp_register_style & wp_enqueue_style, потому что эта ошибка всё ещё добавляет теги <Link />
в подвал через эти функции.
Я мог бы использовать add_action("wp_enqueue_scripts", "style_my_new_map")
вместо add_action('wp-head', "style_my_new_map")
, но насколько я понимаю и проверял, разницы нет? Стили всё равно добавляются на все страницы.
Так что мой вопрос состоит из двух частей: :)
- Почему текущий functions.php не работает и добавляет стили на все страницы? Не уверен, проблема в вызове
global
или в условииif
... - Какой лучший способ добавить стили только в заголовок тех страниц, где используется указанный шорткод, при условии что я не знаю ID страниц и т.д.?
Заранее спасибо! P

Динамическая загрузка скриптов и стилей с помощью шорткода
Преимущества
- Не выполняет поиск по всем записям каждый раз при вызове шорткода.
- Позволяет динамически добавлять стили и скрипты только когда шорткод присутствует на странице.
- Не использует регулярные выражения, так как они работают медленнее, чем
strstr()
илиstrpos()
. Можно переключиться на регулярки, если требуется обработка аргументов. - Уменьшает количество обращений к файлам
Объяснение кода
Находит шорткоды на странице с помощью хука
save_post
только когда запись не является ревизией и соответствует указанномуpost_type
.Сохраняет найденные ID записей в виде массива с помощью
add_option()
с автозагрузкой 'yes', если запись еще не существует. В противном случае используетupdate_option()
.Использует хук
wp_enqueue_scripts
для вызова нашей функцииadd_scripts_and_styles()
.Эта функция вызывает
get_option()
для получения массива ID страниц. Если текущий$page_id
присутствует в массиве$option_id_array
, то добавляет скрипты и стили.
Обратите внимание: Я конвертировал код из ООП с использованием пространств имен, поэтому мог что-то упустить. Сообщите в комментариях, если найдете ошибки.
Пример кода: Поиск вхождений шорткода
function find_shortcode_occurences($shortcode, $post_type = 'page')
{
$found_ids = array();
$args = array(
'post_type' => $post_type,
'post_status' => 'publish',
'posts_per_page' => -1,
);
$query_result = new \WP_Query($args);
foreach ($query_result->posts as $post) {
if (false !== strpos($post->post_content, $shortcode)) {
$found_ids[] = $post->ID;
}
}
return $found_ids;
}
function save_option_shortcode_post_id_array( $post_id ) {
if ( wp_is_post_revision( $post_id ) OR 'page' != get_post_type( $post_id )) {
return;
}
$option_name = 'yourprefix-yourshortcode';
$id_array = find_shortcode_occurences($option_name);
$autoload = 'yes';
if (false == add_option($option_name, $id_array, '', 'yes')) update_option($option_name, $id_array);
}
add_action('save_post', 'save_option_shortcode_id_array' );
Пример кода: Динамическое подключение скриптов и стилей через шорткод
function yourshortcode_add_scripts_and_styles() {
$page_id = get_the_ID();
$option_id_array = get_option('yourprefix-yourshortcode');
if (in_array($page_id, $option_id_array)) {
wp_enqueue_script( $handle, $src, $deps, $ver, $footer = true );
wp_enqueue_style( $handle, $src , $deps);
}
}
add_action('wp_enqueue_scripts', 'yourshortcode_add_scripts_and_styles');

Я еще не тестировал это, но это именно тот ответ, который я искал. Кажется, у этого решения минимальные недостатки.

@JacobRaccuia Я тестировал это некоторое время назад, и это работало. Не знаю, как это будет работать с новыми версиями WP, но сомневаюсь, что многое изменилось, учитывая природу платформы.

Этот вариант работает у меня:
function register_my_css () {
wp_register_style('my-style', plugins_url('styles.css', __FILE__));
}
function register_my_scripts () {
wp_register_script('my-script', plugins_url('scripts.js', __FILE__));
}
// Подключаем скрипты/стили только при вызове шорткода
function do_my_shortcode () {
wp_enqueue_style('my-style');
wp_enqueue_script('my-script');
// выполняем действия шорткода...
}
// Регистрируем CSS
add_action('wp_enqueue_scripts', 'register_my_css');
// Регистрируем JavaScript
add_action('wp_enqueue_scripts', 'register_my_scripts');
// Регистрируем шорткод
add_shortcode('my-shortcode', 'do_my_shortcode');
Подробнее здесь: http://mikejolley.com/2013/12/sensible-script-enqueuing-shortcodes/

Нет, это выведет шорткод поверх контента.

2 вопроса, значит 2 ответа :)
Почему текущий functions.php не работает и добавляет стили на все страницы? Не уверен, проблема в глобальном вызове или в условии if...
Это не работает из-за порядка вызова функций. Ваш шорткод вызывается во время отрисовки контента записи (скорее всего при вызове the_content
).
И wp_enqueue_scripts
, и wp_head
вызываются гораздо раньше: wp_head
где-то в файле header.php
вашей темы, а wp_enqueue_scripts
во время вызова wp_head
.
Какой лучший способ добавить таблицы стилей в заголовок только тех страниц, где используется указанный выше шорткод, учитывая что я не буду знать ID страниц и т.д.?
Не думаю, что существует "лучший" способ. Вообще это не очень хорошая идея. Браузеры кэшируют CSS файлы. Поэтому если у вас одинаковый CSS на всех страницах, браузер загрузит его только один раз. Если же будет много CSS файлов для разных страниц, браузеру придется загружать их все. Так что я бы вообще не стал этого делать и включил эти стили в глобальный CSS файл.
Хотя если вам обязательно нужно подключать стили только на страницах с этим шорткодом, тогда (2 решения, выбор за вами; мне кажется второе более изящным)...
- Можно добавить эти стили как inline-стили. Главное убедиться, что они не будут добавлены несколько раз. То есть выводить их вместе с шорткодом и гарантировать однократный вывод.
- Так как записи уже выбраны, когда вызывается
wp_head
/wp_enqueue_scripts
, можно добавить функцию на этот хук, пройтись по выбранным записям, проверить содержат ли они ваш шорткод и подключить CSS если нужно. (Конечно это не очень эффективно).

Вы можете подключиться к действию после выполнения основного запроса и определить, нужно ли загружать ваши стили и скрипты.
add_action('template_include', 'custom_template_include');
function custom_template_include($template) {
if (is_single() || is_page()) {
global $post;
if (has_shortcode($post->post_content, 'your_short_code')) {
add_action('wp_enqueue_scripts', 'custom_enqueue_scripts');
}
}
return $template;
}
function custom_enqueue_scripts() {
// Подключаем здесь необходимые скрипты и стили
}

add_action( 'wp_enqueue_scripts', 'enqueue_shortcode_styles', 1 );
function enqueue_shortcode_styles() {
global $post;
if ( isset($post->post_content) && is_singular(array( 'post','page' ) ) && ! has_shortcode( $post->post_content, '$tag' ) ) {
wp_enqueue_style( 'shortcode-styles', get_stylesheet_directory_uri() . '/style.css' );
}
}
Я бы использовал has_shortcode
для проверки содержимого $post и загрузки скриптов с помощью wp_enqueue_scripts, но это зависит от того, где встроены ваши шорткоды.
Замените $tag на ваш тег шорткода в приведенном выше коде.
Добавьте этот код в файл functions.php вашей дочерней темы и при необходимости измените условия.

Более простое решение, которое сработало у меня - зарегистрировать скрипт вне функции шорткода, а затем подключить его внутри функции. Таким образом, скрипт загружается только на страницах, использующих ваш шорткод.
wp_register_script('make-a-new-map', get_template_directory_uri() . '/js/new-map.js', array('jquery'), '0.1', true);
wp_register_style('make-a-new-map-style', get_template_directory_uri() . '/css/new-map.css');
add_shortcode("make-me-a-map", "create_new_map");
add_action("wp_head", "style_my_new_map");
function create_new_map () {
wp_enqueue_script('make-a-new-map');
wp_enqueue_style('make-a-new-map-style');
global $new_map_called_for;
$map_called_for = true;
}
Описание этого метода можно найти здесь: http://mikejolley.com/2013/12/sensible-script-enqueuing-shortcodes/
Обратите внимание, что в этом случае CSS загружается в подвале сайта (footer), что может быть нежелательно или не соответствовать стандартам W3C. В моём случае это не было проблемой.

Вы можете сделать это следующим образом:
// хук шорткода
add_shortcode("make-me-a-map", "create_new_map");
// функция обратного вызова шорткода
function style_my_new_map() {
global $new_map_called_for;
if ($new_map_called_for == true) {
wp_register_script('make-a-new-map', get_template_directory_uri() . '/js/new-map.js', array('jquery'), '0.1', true);
wp_enqueue_script('make-a-new-map');
// остальной ваш код
}
}
Это также работает и для таблиц стилей. Чтобы загрузить вашу таблицу стилей последней, добавьте цифры к хуку шорткода, вот так:
add_shortcode("make-me-a-map", "create_new_map", 9999);
Я протестировал это, и это работает.

Динамическая загрузка скриптов и стилей с использованием шорткода
Это расширенный код для решения, предложенного Commandz выше.
Я обнаружил, что его метод является лучшим и более удобным, так как опция базы данных доступна практически на всех хуках.
Код изменен для поиска вхождений шорткодов только в контенте текущей записи, вместо просмотра всех записей - что может быть медленнее при большом количестве записей.
Изменения внесены в две строки после передачи дополнительной переменной $post_id из действия save_post в функцию find_shortcode_occurences()
if( !empty($post_id) ) {
$args['posts__in'] = $post_id;
}
Измененный код для обеих функций
function find_shortcode_occurences($shortcode, $post_id='', $post_type = 'page') {
$found_ids = array();
$args = array(
'post_type' => $post_type,
'post_status' => 'publish',
'posts_per_page' => -1,
);
if( !empty($post_id) ) {
$args['posts__in'] = $post_id;
}
$query_result = new WP_Query($args);
foreach ($query_result->posts as $post) {
if (false !== strpos($post->post_content, $shortcode)) {
$found_ids[] = $post->ID;
}
}
return $found_ids;
}
add_action('save_post', 'save_option_for_sppro_pages' );
function save_option_for_sppro_pages( $post_id ) {
if ( wp_is_post_revision( $post_id ) ) {
return;
}
$option_name = 'database-option';
$shortcode = 'shortcode-name';
$id_array = find_shortcode_occurences($shortcode, $post_id);
$autoload = 'yes';
if (false == add_option($option_name, $id_array, '', 'yes')) {
$current_value = get_option($option_name);
$new_value = array_merge($current_value, $id_array);
update_option($option_name, $id_array);
}
}
Также добавлена еще одна переменная, чтобы можно было использовать разные имена для опции базы данных и имени шорткода.
