Подключение скриптов и стилей при наличии шорткода

18 окт. 2014 г., 10:07:40
Просмотры: 69.4K
Голосов: 87

Какой оптимальный способ регистрации/подключения скриптов и/или стилей для использования в плагинах?

Недавно я создал простой плагин для добавления аватара пользователя/граватара с помощью шорткода. У меня есть различные варианты стилей для отображения аватара (квадратный, круглый и т.д.), и я решил поместить CSS непосредственно в сам шорткод.

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

Вот методы, о которых мне известно:

Метод 1: Включение напрямую в шорткод - Это то, что я сейчас использую в плагине, но кажется неоптимальным, так как код повторяется.

class My_Shortcode {
function handle_shortcode( $atts, $content="" ) {
/* просто подключаем или выводим скрипты/стили прямо в шорткоде */
?>
<style type="text/css">

</style>
<?php
    return "$content";
     }
}
add_shortcode( 'myshortcode', array( 'My_Shortcode', 'handle_shortcode' ) );

Метод 2: Использование класса для условного подключения скриптов или стилей

class My_Shortcode {
    static $add_script;
    static function init() {
        add_shortcode('myshortcode', array(__CLASS__, 'handle_shortcode'));
        add_action('init', array(__CLASS__, 'register_script'));
        add_action('wp_footer', array(__CLASS__, 'print_script'));
    }
    static function handle_shortcode($atts) {
        self::$add_script = true;
        // обработка шорткода здесь
    }
    static function register_script() {
        wp_register_script('my-script', plugins_url('my-script.js', __FILE__), array('jquery'), '1.0', true);
    }
    static function print_script() {
        if ( ! self::$add_script )
            return;
        wp_print_scripts('my-script');
    }
}
My_Shortcode::init();

Метод 3: Использование get_shortcode_regex();

function your_prefix_detect_shortcode() {

    global $wp_query;   
    $posts = $wp_query->posts;
    $pattern = get_shortcode_regex();

    foreach ($posts as $post){
        if (   preg_match_all( '/'. $pattern .'/s', $post->post_content, $matches )
            && array_key_exists( 2, $matches )
            && in_array( 'myshortcode', $matches[2] ) )
        {
            // css/js 
            break;  
        }    
    }
}
add_action( 'wp', 'your_prefix_detect_shortcode' );

Метод 4: Использование has_shortcode();

function custom_shortcode_scripts() {
    global $post;
    if( is_a( $post, 'WP_Post' ) && has_shortcode( $post->post_content, 'myshortcode') ) {
        wp_enqueue_script( 'my-script');
    }
}
add_action( 'wp_enqueue_scripts', 'custom_shortcode_scripts');
4
Комментарии

Я считаю, что Метод 4: Использование has_shortcode(); лучше всего, потому что он гарантирует загрузку скриптов и стилей только один раз, если в контенте записи есть шорткод, независимо от количества его использований. Хотя он может не работать для шорткодов, используемых в виджетах или сайдбаре, я не уверен. Если это для плагина, то я не рекомендовал бы привязывать скрипты к шорткоду, потому что некоторые могут вызывать вашу функцию вместо шорткода для получения желаемого вывода.

Robert hue Robert hue
18 окт. 2014 г. 10:46:54

Метод 4 хорош, но есть одна проблема - он может создать сложности, если шорткод добавлен не напрямую в контентную область WP, а через опции стороннего плагина. Может быть полезно добавить проверку в функцию шорткода ! wp_style_is вместе с методом 4 и подключить стиль там, если он не был добавлен ранее в колбэке wp_enqueue_scripts

Adeel Raza Adeel Raza
14 окт. 2020 г. 13:23:43

Метод 4 не будет работать, если запрашиваемая страница является домашней, индексной страницей, и самая первая запись не содержит шорткода. Все остальные записи не будут отображать CSS и JS шорткода корректно.

vee vee
1 янв. 2023 г. 21:17:19
Все ответы на вопрос 10
4
88

Я нашел другой способ, который хорошо работает для меня:

Вот пример плагина, использующего этот метод, который позволяет использовать get_avatar в шорткоде. Таблица стилей подключается только при наличии шорткода.

Использование (id по умолчанию - текущий пользователь):

[get_avatar id="" size="32" default="mystery" alt="Фото профиля" class="round"]

function wpse_165754_avatar_shortcode_wp_enqueue_scripts() {
    wp_register_style( 'get-avatar-style', plugins_url( '/css/style.css', __FILE__ ), array(), '1.0.0', 'all' );
}

add_action( 'wp_enqueue_scripts', 'wpse_165754_avatar_shortcode_wp_enqueue_scripts' );
if ( function_exists( 'get_avatar' ) ) {
    function wpse_165754_user_avatar_shortcode( $attributes ) {

        global $current_user;
        get_currentuserinfo();

        extract( shortcode_atts(
                     array(
                         "id"      => $current_user->ID,
                         "size"    => 32,
                         "default" => 'mystery',
                         "alt"     => '',
                         "class"   => '',
                     ), $attributes, 'get_avatar' ) );
        
        $get_avatar = get_avatar( $id, $size, $default, $alt );

        wp_enqueue_style( 'get-avatar-style' );

        return '<span class="get_avatar ' . $class . '">' . $get_avatar . '</span>';
    }

    add_shortcode( 'get_avatar', 'wpse_165754_user_avatar_shortcode' );
}
15 июн. 2015 г. 15:28:58
Комментарии

Забыл, что задавал этот вопрос в прошлом году, но я тоже во многих случаях начал делать именно так. Это что-то вроде комбинации первого и второго методов.

Bryan Willis Bryan Willis
8 июл. 2015 г. 20:36:53

Этот метод загружает CSS в подвале, что наверняка имеет ограничения?

Jacob Raccuia Jacob Raccuia
21 апр. 2017 г. 16:26:32

Это абсолютно правильный способ. Используйте этот метод всякий раз, когда вам нужно условно загрузить скрипт или таблицу стилей после того, как хук wp_enqueue_scripts уже произошел. (Шорткоды обрабатываются во время the_content, который является частью хука the_post, а значит, подключение во время их обработки будет слишком поздним, если вы уже не зарегистрировали скрипт заранее)

JRad the Bad JRad the Bad
21 апр. 2019 г. 00:56:49

Это не полезно, так как создает FOUT (мигание нестилизованного текста), когда стиль загружается в функции шорткода. Лучше использовать метод has_shortcode.

Adeel Raza Adeel Raza
14 окт. 2020 г. 13:27:58
3
35

Прежде чем начать ответ, хочу сказать, что в данном вопросе CSS и JS — это не одно и то же.

Причина проста: хотя добавление JS в тело страницы (в подвал) является распространённым и допустимым подходом, CSS необходимо размещать в секции <head> страницы. Даже если большинство браузеров корректно обрабатывают CSS в теле страницы, это не соответствует валидному HTML-коду.

Когда шорткод обрабатывается, секция <head> уже выведена. Это означает, что JS можно без проблем добавить в подвал, но CSS должен быть добавлен до отображения шорткода.

Скрипты

Если ваш шорткод использует только JS, вам повезло — можно просто использовать wp_enqueue_script в теле callback-функции шорткода:

add_shortcode( 'myshortcode', 'my_handle_shortcode' );

function my_handle_shortcode() {
  wp_enqueue_script( 'myshortcodejs', '/path/to/js/file.js' );
  // остальной код здесь...
}

Таким образом, ваш скрипт добавится в подвал и только один раз, даже если шорткод используется на странице несколько раз.

Стили

Если ваш код требует стилей, их нужно подключить до отображения шорткода.

Есть несколько способов сделать это:

  1. Проверить все записи в текущем запросе и добавить стили шорткода, если это необходимо. Именно это делается в методах #3 и #4 из оригинального вопроса. Оба метода выполняют одно и то же, но has_shortcode появился в WordPress 3.6, а get_shortcode_regex доступен с версии 2.5. Используйте get_shortcode_regex, только если нужно обеспечить совместимость с более старыми версиями.

  2. Всегда добавлять стили шорткода на всех страницах.

Проблемы

Проблема с первым подходом — производительность. Регулярные выражения довольно медленные, и их выполнение в цикле для всех записей может значительно замедлить загрузку страницы. Кроме того, в темах часто показывают только анонсы записей в архивах, а полный контент с шорткодами — только на отдельных страницах. В таком случае, при отображении архива, ваш плагин будет запускать проверку регулярных выражений в цикле, чтобы добавить стили, которые никогда не будут использованы. Это двойной удар по производительности: замедление генерации страницы + лишний HTTP-запрос.

Проблема второго подхода — снова производительность. Добавление стилей на все страницы означает лишний HTTP-запрос везде, даже там, где он не нужен. Хотя время генерации страницы на сервере не пострадает, общее время её отображения увеличится для всех страниц сайта.

Что же делать разработчику плагина?

Лучшее решение — добавить страницу настроек в плагин, где пользователи смогут выбрать, должен ли шорткод работать только на отдельных страницах или в архивах тоже. В обоих случаях хорошо предоставить ещё одну опцию для выбора типов записей, где шорткод будет активен.

Таким образом, можно использовать хук "template_redirect", чтобы проверить, соответствует ли текущий запрос требованиям, и, если да, добавить стили.

Если пользователь выбрал использование шорткода только на отдельных страницах, стоит проверить, есть ли в записи шорткод. Поскольку требуется всего одно регулярное выражение, это не должно сильно замедлить загрузку.

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

Что считать "большим" количеством, можно определить с помощью тестов производительности или, как вариант, добавить ещё одну настройку и дать выбор пользователям.

18 окт. 2014 г. 11:49:56
Комментарии

Стоит отметить, что теги <style> в теле документа теперь считаются валидными согласно спецификации W3C. https://www.w3.org/TR/html52/document-metadata.html#the-style-element

Isaac Lubow Isaac Lubow
17 нояб. 2018 г. 00:30:40

@IsaacLubow На текущий момент (2023): Контексты, в которых может использоваться этот элемент: Внутри элемента noscript, который является дочерним для элемента head. Проверил валидность <style> внутри <body> на https://validator.w3.org/nu/#textarea, но результат - Ошибка: Элемент style не допускается как дочерний для элемента body в данном контексте.

vee vee
1 янв. 2023 г. 20:53:56

Однако <link> внутри <body> проходит валидацию.

vee vee
1 янв. 2023 г. 20:56:10
0
11

Для своего плагина я обнаружил, что иногда пользователи используют конструктор тем, который хранит шорткоды в метаданных записи. Вот что я использую для определения, присутствует ли шорткод моего плагина в текущей записи или её метаданных:

function abcd_load_my_shorcode_resources() {
       global $post, $wpdb;

       // определяем, содержит ли страница шорткод "my_shortcode"
       $shortcode_found = false;
       if ( has_shortcode($post->post_content, 'my_shortcode') ) {
          $shortcode_found = true;
       } else if ( isset($post->ID) ) {
          $result = $wpdb->get_var( $wpdb->prepare(
            "SELECT count(*) FROM $wpdb->postmeta " .
            "WHERE post_id = %d and meta_value LIKE '%%my_shortcode%%'", $post->ID ) );
          $shortcode_found = ! empty( $result );
       }

       if ( $shortcode_found ) {
          wp_enqueue_script(...);
          wp_enqueue_style(...);
       }
}
add_action( 'wp_enqueue_scripts', 'abcd_load_my_shorcode_resources' );
6 нояб. 2015 г. 02:18:02
0

WordPress имеет встроенную функцию для выполнения действий в зависимости от наличия определенного шорткода. Называется она has_shortcode(). Вы можете использовать следующий код для подключения стилей и скриптов.

Здесь я использовал is_a( $post, 'WP_Post' ) для проверки, является ли объект $post экземпляром класса WP_Post, а также $post->post_content для проверки содержимого записи.

if ( is_a( $post, 'WP_Post' ) && has_shortcode( $post->post_content, 'shortcode_tag') ) {
    wp_enqueue_style( 'handle', get_template_directory_uri() . '/your_file_filename.css' );
}
7 окт. 2018 г. 20:04:30
0

Я делаю так:

class My_Shortcode {

    function __construct() {
        do_action('my_start_shortcode'); // вызываем хук
....

и ловлю хук в других функциях (или других плагинах):

function wpse_3232_load_script(){
    wp_enqueue_script('js-myjs'); // подключаем скрипт
}
add_action('my_start_shortcode','wpse_3232_load_script',10); // вешаем функцию на хук
8 авг. 2017 г. 12:51:20
0

Как уже упоминалось, использование 'has_shortcode' решит эту задачу. Вот пример, который я нашел очень полезным (Источник: Wordpress.org)

function wpdocs_shortcode_scripts() {
    global $post;
    if ( is_a( $post, 'WP_Post' ) && has_shortcode( $post->post_content, 'wpdocs-shortcode') ) {
        wp_enqueue_script( 'wpdocs-script');
    }
}
add_action( 'wp_enqueue_scripts', 'wpdocs_shortcode_scripts');
27 янв. 2021 г. 22:07:44
1

Мы действительно можем условно загружать CSS и JS файлы через шорткод, не используя слишком сложные функции:

Сначала предположим, что мы зарегистрировали все наши CSS/JS файлы заранее (например, при выполнении wp_enqueue_scripts):

function prep_css_and_js() {
     wp_register_style('my-css', $_css_url);
     wp_register_script('my-js', $_js_url);
}
add_action( 'wp_enqueue_scripts', 'prep_css_and_js', 5 );

Далее рассмотрим нашу функцию шорткода:

function my_shortcode_func( $atts, $content = null ) {
  if( ! wp_style_is( "my-css", $list = 'enqueued' ) ) { wp_enqueue_style('my-css'); }
  if( ! wp_script_is( "my-js", $list = 'enqueued' ) ) { wp_enqueue_script('my-js'); }
  ...
}

Просто. Готово. Таким образом: мы можем "условно загружать" CSS/JS файлы (только при выполнении [шорткода]).

Рассмотрим следующую идеологию:

  • Мы хотим загружать определённые CSS/JS файлы (но только когда срабатывает шорткод)
  • Возможно, мы не хотим, чтобы эти файлы загружались на каждой странице или вообще, если шорткод не используется на странице/записи. (лёгкость)
  • Мы можем достичь этого, убедившись, что WP регистрирует ВСЕ CSS/JS файлы до вызова шорткода и до постановки в очередь.
  • То есть: возможно, во время моей функции prep_css_and_js() я загружаю ВСЕ .css/.js файлы из определённых папок...

Теперь, когда WP видит их как зарегистрированные скрипты, мы действительно можем вызывать их условно, основываясь на различных ситуациях, включая, но не ограничиваясь my_shortcode_func()

Преимущества: (без MySQL, без сложных функций и без необходимости фильтров)

  • это "ортодоксально для WP", элегантно, быстро загружается, легко и просто
  • позволяет компиляторам/минификаторам обнаруживать такие скрипты
  • использует WP функции (wp_register_style/wp_register_script)
  • совместимо с большим количеством тем/плагинов, которые могут захотеть отменить регистрацию этих скриптов по разным причинам.
  • не срабатывает несколько раз
  • задействовано очень мало обработки или кода

Ссылки:

16 сент. 2019 г. 00:00:38
Комментарии

Этот метод загружает стили ниже футера. Обычно лучше размещать их в head.

leclaeli leclaeli
8 мая 2024 г. 23:24:14
0

Я обнаружил, как проверить наличие шорткода в текстовом виджете для моего собственного плагина.

function check_shortcode($text) {

  $pattern = get_shortcode_regex();

   if (   preg_match_all( '/'. $pattern .'/s', $text, $matches )
        && array_key_exists( 2, $matches )
        && in_array( 'myshortcode', $matches[2] ) )
    {
        // шорткод используется

        // подключаем мои CSS и JS
        /****** Подключение RFNB  ******/
        wp_enqueue_style('css-mycss');
        wp_enqueue_script('js-myjs');

        // ОПЦИОНАЛЬНО: РАЗРЕШАЕМ РАБОТУ ШОРТКОДА В ТЕКСТОВОМ ВИДЖЕТЕ
        add_filter( 'widget_text', 'shortcode_unautop');
        add_filter( 'widget_text', 'do_shortcode'); 

    }

  return $text;

}
add_filter('widget_text', 'check_shortcode');
21 дек. 2016 г. 18:33:31
0

Я объединил пример кода со страницы WordPress для has_shortcode() и ответа пользователя zndencka. Я заметил, что функция has_shortcode была добавлена в WordPress 3.6, поэтому сначала проверяю её существование. Хотя, возможно, эта проверка уже устарела, так как, согласно статистике самого WordPress, пользователей с версией ниже 3.6 осталось очень мало.

// Это гарантирует, что стили будут подключены в заголовке до загрузки шорткода на странице/записи
function enqueue_shortcode_header_script() {
    global $post;
    if ( function_exists( 'has_shortcode' ) ){  // добавлено в WordPress 3.6
        // Источник: https://codex.wordpress.org/Function_Reference/has_shortcode
        if( is_a( $post, 'WP_Post' ) && has_shortcode( $post->post_content, 'my_shortcode') ) {
            wp_enqueue_style( 'shortcode-css' );
        }
    }
    else if ( isset($post->ID) ) { // Небольшой процент пользователей WordPress ниже 3.6 https://wordpress.org/about/stats/
        global $wpdb;
        $result = $wpdb->get_var(
          $wpdb->prepare(
              "SELECT count(*) FROM $wpdb->postmeta " .
              "WHERE post_id = %d and meta_value LIKE '%%my_shortcode%%'",
               $post->ID )
        );
        if (!empty( $result )) { wp_enqueue_style( 'shortcode-css' ); }
    }
}
add_action( 'wp_enqueue_scripts', 'enqueue_shortcode_header_script');
12 апр. 2019 г. 16:35:39
0

Я использую WordPress версии 5.4 с ООП стилем кода, не знаю, влияет ли это на то, почему ни одно из вышеперечисленных решений не сработало для меня, поэтому я придумал такое решение:

public function enqueue_scripts() {
 global $post;
 //error_log( print_r( $post, true ) );
 //error_log( print_r(  $post->post_content, true ) );
 //error_log( print_r(  strpos($post->post_content, '[YOUR_SHORTCODE]'),true));

 if ( is_a( $post, 'WP_Post' ) && strpos($post->post_content, '[YOUR_SHORTCODE]') ) 
 {
     wp_register_style('my-css', $_css_url);
     wp_register_script('my-js', $_js_url);
 }
}

Надеюсь, это поможет кому-то.

14 апр. 2020 г. 18:02:52