Загрузка скриптов только при наличии определенного шорткода или виджета
Мне понадобился способ фильтрации контента страницы/записи перед загрузкой, чтобы добавить скрипты в заголовок, если присутствует определенный шорткод. После долгих поисков я нашел решение на http://wpengineer.com:
function has_my_shortcode($posts) {
if ( empty($posts) )
return $posts;
$found = false;
foreach ($posts as $post) {
if ( stripos($post->post_content, '[my_shortcode') )
$found = true;
break;
}
if ($found){
$urljs = get_bloginfo( 'template_directory' ).IMP_JS;
wp_register_script('my_script', $urljs.'myscript.js' );
wp_print_scripts('my_script');
}
return $posts;
}
add_action('the_posts', 'has_my_shortcode');
Это решение просто великолепно и полностью соответствует моим потребностям.
Теперь мне нужно расширить его функциональность и сделать то же самое для сайдбаров. Это может быть реализовано через определенный тип виджета, шорткод, фрагмент кода или любой другой способ, который позволит определить, когда нужно загружать скрипт.
Проблема в том, что я не могу понять, как получить доступ к содержимому сайдбаров до их загрузки (в рассматриваемой теме используется несколько сайдбаров).

Вы можете использовать функцию is_active_widget. Например:
function check_widget() {
if( is_active_widget( '', '', 'search' ) ) { // проверяем, используется ли виджет поиска
wp_enqueue_script('my-script');
}
}
add_action( 'init', 'check_widget' );
Чтобы загрузить скрипт только на странице, где загружен виджет, вам нужно добавить код is_active_widget() в класс вашего виджета. Например, посмотрите стандартный виджет последних комментариев (wp-includes/default-widgets.php, строка 602):
class WP_Widget_Recent_Comments extends WP_Widget {
function WP_Widget_Recent_Comments() {
$widget_ops = array('classname' => 'widget_recent_comments', 'description' => __( 'Последние комментарии' ) );
$this->WP_Widget('recent-comments', __('Последние комментарии'), $widget_ops);
$this->alt_option_name = 'widget_recent_comments';
if ( is_active_widget(false, false, $this->id_base) )
add_action( 'wp_head', array(&$this, 'recent_comments_style') );
add_action( 'comment_post', array(&$this, 'flush_widget_cache') );
add_action( 'transition_comment_status', array(&$this, 'flush_widget_cache') );
}

Можно ли вызвать это ДО загрузки виджетов. Чтобы было понятно, это должно произойти еще до загрузки страницы. В моем примере кода используется the_posts для фильтрации контента на странице, но это не затрагивает сайдбары/виджеты.

Твой пример на самом деле не работает. Я что-то упускаю из виду?

На самом деле, так и было. wp_enqueue_script, похоже, не работает при применении к wp_head, но wp_print_scripts работает. Однако wp_enqueue_script действительно работает, если применять его к init вот так: add_action( 'init', 'check_widget' );

Однако он загружает скрипты на каждой странице сайта, даже если виджет активен только в одной боковой панели. Например, если у меня есть боковая панель для страниц блога и другая боковая панель для остальных страниц. Я добавляю виджет поиска в боковую панель блога, и скрипты загружаются, но они также загружаются на страницах, где нет боковой панели блога. Мне нужно, чтобы скрипт загружался только если виджет присутствует на данной странице.

Хочу поделиться решением, над которым работал, которое позволило сделать мою реализацию более универсальной. Вместо того чтобы заставлять функцию проверять множество шорткодов, я модифицировал её для поиска одного шорткода, который ссылается на функцию скрипта/стиля, требующую подключения. Это будет работать как для методов в других классах (например, плагинов, которые могут не делать этого самостоятельно), так и для функций в текущей области видимости. Просто вставьте приведённый ниже код в functions.php и добавьте следующий синтаксис на любую страницу/запись, где нужны определённые CSS и JS.
Синтаксис для страницы/записи: [loadCSSandJS function="(класс->метод/имя функции)"]
Код для functions.php:
function page_specific_CSSandJS( $posts ) {
if ( empty( $posts ) )
return $posts;
foreach ($posts as $post) {
$returnValue = preg_match_all('#\[loadCSSandJS function="([A-z\-\>]+)"\]#i', $post->post_content, $types);
foreach($types[1] as $type) {
$items = explode("->",$type);
if (count($items) == 2) {
global $$items[0];
if( method_exists( $$items[0], $items[1] ))
add_action( 'wp_enqueue_scripts', array(&$$items[0], $items[1]) );
}
else if( !empty( $type ) && function_exists( $type ))
add_action( 'wp_enqueue_scripts', $type );
}
}
return $posts;
}
Это также позволит добавлять любое количество таких вызовов на страницу. Учтите, что у вас должен быть метод/функция для загрузки.

В WordPress 4.7 появился еще один способ сделать это в вашем файле functions.php
,
add_action( 'wp_enqueue_scripts', 'register_my_script');
function register_my_script(){
wp_register_script('my-shortcode-js',$src, $dependency, $version, $inFooter);
}
Сначала вы регистрируете свой скрипт, который фактически не выводится на вашей странице, пока вы не подключите его, если вызывается ваш шорткод,
add_filter( 'do_shortcode_tag','enqueue_my_script',10,3);
function enqueue_my_script($output, $tag, $attr){
if('myShortcode' != $tag){ //убеждаемся, что это правильный шорткод
return $output;
}
if(!isset($attr['id'])){ //можно даже проверить наличие определенных атрибутов
return $output;
}
wp_enqueue_script('my-shortcode-js'); //подключаем ваш скрипт для вывода
return $output;
}
Фильтр do_shortcode_tag
был добавлен в WP 4.7 и его можно найти на новых страницах документации для разработчиков.

Спасибо за этот совет!
Он доставил мне небольшую головную боль, потому что сначала не работал. Я думаю, что в коде выше has_my_shortcode
есть пара ошибок (возможно, опечаток), и я переписал функцию следующим образом:
function has_my_shortcode( $posts ) {
if ( empty($posts) )
return $posts;
$shortcode_found = false;
foreach ($posts as $post) {
if ( !( stripos($post->post_content, '[my-shortcode') === false ) ) {
$shortcode_found = true;
break;
}
}
if ( $shortcode_found ) {
$this->add_scripts();
$this->add_styles();
}
return $posts;
}
Я думаю, было две основные проблемы: break
не находился в области видимости оператора if, из-за чего цикл всегда прерывался на первой итерации. Вторая проблема была более тонкой и сложной для обнаружения. Код выше не работает, если шорткод является самым первым элементом в записи. Мне пришлось использовать оператор ===
чтобы решить это.
В любом случае, большое спасибо за эту технику, она очень помогла.
