Cargar Scripts / Estilos cuando el shortcode está presente

18 oct 2014, 10:07:40
Vistas: 69.4K
Votos: 87

¿Cuál es la mejor manera de registrar/cargar scripts y/o estilos para usar en plugins?

Recientemente hice un plugin simple para añadir el avatar/gravatar del usuario con un shortcode. Tengo diferentes opciones de estilo para mostrar el avatar (cuadrado, redondo, etc.) y decidí poner el CSS directamente en el shortcode mismo.

Sin embargo, ahora me doy cuenta de que este no es un buen enfoque ya que repetirá el CSS cada vez que se use el shortcode en una página. He visto varios otros enfoques en este sitio y el codex de WordPress incluso tiene dos ejemplos propios, por lo que es difícil saber qué enfoque es más consistente y rápido.

Aquí están los métodos que conozco actualmente:

Método 1: Incluir directamente en el shortcode - Esto es lo que estoy haciendo actualmente en el plugin, pero no parece bueno ya que repite código.

class My_Shortcode {
    function handle_shortcode( $atts, $content="" ) {
    /* simplemente cargar o imprimir los scripts/estilos en el shortcode mismo */
    ?>
    <style type="text/css">

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

Método 2: Usar clase para cargar scripts o estilos condicionalmente

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;
        // manejo del shortcode aquí
    }
    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();

Método 3: Usando 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' );

Método 4: Usando 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
Comentarios

Creo que el Método 4: Usar has_shortcode(); es el mejor porque asegurará que los scripts y estilos se carguen una vez si el contenido del post tiene el shortcode, independientemente de los usos múltiples del shortcode. Aunque podría no funcionar para shortcodes usados en widgets o en la barra lateral, no estoy seguro. Si es para un plugin, entonces no te recomendaría vincular los scripts con el shortcode porque algunos podrían llamar a tu función en lugar del shortcode para obtener el resultado deseado.

Robert hue Robert hue
18 oct 2014 10:46:54

El Método 4 es bueno, pero hay un problema: puede crear un inconveniente si el shortcode no se agrega directamente al área de contenido de WP sino a opciones de otros plugins de terceros. Podría ser útil agregar una verificación en la función del shortcode ! wp_style_is junto con el método 4 y encolar el estilo allí en caso de que no se haya agregado en la devolución de llamada anterior wp_enqueue_scripts

Adeel Raza Adeel Raza
14 oct 2020 13:23:43

El Método 4 no funcionará si la página solicitada es algo como la página de inicio, página índice y el primer post no tiene shortcode. Todos los demás posts no mostrarán correctamente el CSS y JS del shortcode.

vee vee
1 ene 2023 21:17:19
Todas las respuestas a la pregunta 10
4
88

Encontré otra forma que funciona bien para mí:

Aquí tienes un ejemplo de plugin que usa este método y te permite usar get_avatar en un shortcode. La hoja de estilos solo se encola cuando el shortcode está presente.

Uso (el id por defecto es el usuario actual):

[get_avatar id="" size="32" default="mystery" alt="Foto de perfil" 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 jun 2015 15:28:58
Comentarios

Olvidé que hice esta pregunta el año pasado, pero de hecho también había comenzado a hacerlo de esta manera en muchos casos. Es como combinar el método 1 y 2.

Bryan Willis Bryan Willis
8 jul 2015 20:36:53

Este método carga el CSS en el pie de página, lo cual seguramente tiene limitaciones, ¿no?

Jacob Raccuia Jacob Raccuia
21 abr 2017 16:26:32

Esta es absolutamente la forma correcta de hacerlo. Usa este método cada vez que necesites cargar condicionalmente un script o hoja de estilo cuando el hook wp_enqueue_scripts ya haya ocurrido. (Los shortcodes se analizan durante the_content que es parte del hook the_post, lo que significa que encolarlos cuando se analizan es demasiado tarde a menos que ya hayas registrado el script).

JRad the Bad JRad the Bad
21 abr 2019 00:56:49

Esto no es útil ya que crea FOUT cuando el estilo se carga en la función del shortcode. Es mejor usar el método has_shortcode.

Adeel Raza Adeel Raza
14 oct 2020 13:27:58
3
35

Antes de comenzar, debo decir que en este tema CSS y JS no son lo mismo.

La razón es simple: mientras que agregar JS al cuerpo de la página (en el footer) es una forma común y válida, el CSS debe colocarse en la sección <head> de la página: aunque la mayoría de los navegadores pueden renderizar correctamente el CSS en el cuerpo de la página, eso no es código HTML válido.

Cuando se renderiza un shortcode, la sección <head> ya se ha impreso, lo que significa que JS se puede agregar sin problemas en el footer, pero el CSS debe agregarse antes de que se renderice el shortcode.

Scripts

Si tu shortcode solo necesita JS, tienes suerte y puedes usar wp_enqueue_script en el cuerpo de la función del shortcode:

add_shortcode( 'myshortcode', 'my_handle_shortcode' );

function my_handle_shortcode() {
  wp_enqueue_script( 'myshortcodejs', '/path/to/js/file.js' );
  // resto del código aquí...
}

De esta manera, tu script se agrega al footer y solo una vez, incluso si el shortcode se usa más de una vez en la página.

Estilos

Si tu código necesita estilos, entonces debes actuar antes de que el shortcode se renderice.

Existen diferentes formas de hacer esto:

  1. Revisar todas las entradas en la consulta actual y agregar los estilos del shortcode si es necesario. Esto es lo que haces en los métodos #3 y #4 del OP. De hecho, ambos métodos hacen lo mismo, pero has_shortcode se agregó en WP 3.6, mientras que get_shortcode_regex está disponible desde la versión 2.5, así que usa get_shortcode_regex solo si deseas que tu plugin sea compatible con versiones antiguas.

  2. Agregar siempre los estilos del shortcode, en todas las páginas

Problemas

El problema con #1 es el rendimiento. Las expresiones regulares (regex) son operaciones bastante lentas y ejecutarlas en un bucle de todas las entradas puede ralentizar considerablemente la página. Además, es una tarea común en los temas mostrar solo el extracto de las entradas en los archivos y mostrar el contenido completo con shortcodes solo en las vistas individuales. Si eso ocurre, cuando se muestra un archivo, tu plugin ejecutará una coincidencia de regex en un bucle con el objetivo de agregar un estilo que nunca se usará: un impacto doble e innecesario en el rendimiento: ralentización de la generación de la página + solicitud HTTP adicional innecesaria.

El problema con #2 también es el rendimiento. Agregar estilos a todas las páginas significa agregar una solicitud HTTP adicional para todas las páginas, incluso cuando no es necesario. Aunque el tiempo de generación de la página en el servidor no se ve afectado, el tiempo total de renderizado de la página sí lo hará, y para todas las páginas del sitio.

Entonces, ¿qué debe hacer un desarrollador de plugins?

Creo que lo mejor es agregar una página de opciones al plugin, donde los usuarios puedan elegir si el shortcode debe manejarse solo en vistas individuales o también en archivos. En ambos casos, es mejor proporcionar otra opción para elegir para qué tipos de contenido habilitar el shortcode.

De esta manera, es posible enlazar "template_redirect" para verificar si la consulta actual cumple con los requisitos y, en ese caso, agregar el estilo.

Si el usuario elige usar el shortcode solo en vistas individuales de entradas, es una buena idea verificar si la entrada tiene el shortcode o no: como solo se requiere una expresión regular, no debería ralentizar demasiado la página.

Si el usuario elige usar el shortcode incluso en archivos, entonces evitaría ejecutar regex para todas las entradas si el número de entradas es alto y simplemente encolar el estilo si la consulta cumple con los requisitos.

Lo que se considera "alto" en este sentido debe determinarse mediante algunas pruebas de rendimiento o, como alternativa, agregar otra opción y darle la elección a los usuarios.

18 oct 2014 11:49:56
Comentarios

Vale la pena mencionar aquí que las etiquetas <style> en el cuerpo del documento ahora son válidas según la especificación W3C. https://www.w3.org/TR/html52/document-metadata.html#the-style-element

Isaac Lubow Isaac Lubow
17 nov 2018 00:30:40

@IsaacLubow Actualmente (2023): Contextos en los que este elemento puede usarse: En un elemento noscript que sea hijo de un elemento head. Validé <style> dentro de <body> en https://validator.w3.org/nu/#textarea pero el resultado es Error: Elemento style no permitido como hijo del elemento body en este contexto.

vee vee
1 ene 2023 20:53:56

Pero <link> dentro de <body> pasa la validación.

vee vee
1 ene 2023 20:56:10
0
11

Para mi plugin, descubrí que a veces los usuarios tienen un constructor de temas que almacena shortcodes en los metadatos del post. Esto es lo que uso para detectar si el shortcode de mi plugin está presente en el post actual o en sus metadatos:

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

       // Determina si esta página contiene el shortcode "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 nov 2015 02:18:02
0

WordPress tiene una función incorporada para realizar acciones basadas en el estado de presentación de un Shortcode específico. El nombre de la función es has_shortcode(). Puedes usar el siguiente código para encolar tus estilos y scripts.

Aquí utilicé is_a( $post, 'WP_Post' ) para verificar si el objeto $post es de la clase WP_Post y usé $post->post_content para verificar el contenido del post.

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 oct 2018 20:04:30
0

Yo lo hago así:

class My_Shortcode {

    function __construct() {
        do_action('my_start_shortcode'); // llamada
....

y capturo el hook en otras funciones (u otros plugins):

function wpse_3232_load_script(){
    wp_enqueue_script('js-myjs');
}
add_action('my_start_shortcode','wpse_3232_load_script',10);
8 ago 2017 12:51:20
0

Como alguien mencionó, usar 'has_shortcode' puede resolverlo. Aquí hay un ejemplo que encontré muy útil (Fuente: 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 ene 2021 22:07:44
1

Podemos cargar condicionalmente archivos CSS y JS mediante shortcodes sin necesidad de usar funciones excesivamente complejas:

Primero, asumamos que hemos registrado todos nuestros archivos CSS/JS previamente (quizás cuando se ejecutó 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 );

A continuación, examinemos nuestra función de shortcode:

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'); }
  ...
}

Sencillo. Listo. Ergo: podemos "cargar condicionalmente" archivos css/js (solo cuando se ejecuta el [shortcode]).

Consideremos la siguiente ideología:

  • Queremos cargar ciertos archivos CSS/JS (pero solo cuando se active el shortcode)
  • Quizás no queremos que esos archivos se carguen en todas las páginas, o que no se carguen si el shortcode no se usa en una página/entrada. (ligero)
  • Podemos lograrlo asegurándonos de que WP registre TODOS los archivos css/js antes de llamar al shortcode y antes de encolarlos.
  • Ejemplo: Quizás durante mi función prep_css_and_js() cargo TODOS los archivos .css/.js que están en ciertas carpetas...

Ahora que WP los ve como scripts registrados, podemos llamarlos condicionalmente, basándonos en diversas situaciones, incluyendo pero no limitado a my_shortcode_func()

Beneficios: (sin MySQL, sin funciones avanzadas y sin necesidad de filtros)

  • esto es "ortodoxo en WP", elegante, de carga rápida, fácil y simple
  • permite que los compresores/minificadores detecten dichos scripts
  • usa las funciones de WP (wp_register_style/wp_register_script)
  • compatible con más temas/plugins que podrían querer desregistrar esos scripts por diversas razones.
  • no se activará múltiples veces
  • muy poco procesamiento o código involucrado

Referencias:

16 sept 2019 00:00:38
Comentarios

Este método carga estilos debajo del footer. Generalmente es mejor ponerlos en el head.

leclaeli leclaeli
8 may 2024 23:24:14
0

Descubrí para mi propio plugin cómo verificar si el shortcode está presente en el widget de texto.

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] ) )
    {
        // El shortcode 'myshortcode' está siendo usado

        // Cargar mi CSS y JS
        /****** Cargar RFNB  ******/
        wp_enqueue_style('css-mycss');
        wp_enqueue_script('js-myjs');

        // OPCIONAL: PERMITIR QUE EL SHORTCODE FUNCIONE EN WIDGETS DE TEXTO
        add_filter( 'widget_text', 'shortcode_unautop');
        add_filter( 'widget_text', 'do_shortcode'); 

    }

  return $text;

}
add_filter('widget_text', 'check_shortcode');
21 dic 2016 18:33:31
0

Hice una combinación del código de ejemplo de la página de Wordpress para has_shortcode() y la respuesta que zndencka proporcionó. Noté que la función has_shortcode se agregó en Wordpress 3.6, por eso primero verifico si la función existe. Tal vez esa verificación sea un poco obsoleta, ya que no hay muchos usuarios con versiones inferiores a WordPress 3.6 según sus propias estadísticas.

// Esto asegura que el estilo ya esté encolado en el encabezado, antes de que el shortcode se cargue en la página/publicación
function enqueue_shortcode_header_script() {
    global $post;
    if ( function_exists( 'has_shortcode' ) ){  // se agregó en wordpress 3.6
        // Fuente: 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) ) { // Un pequeño porcentaje de usuarios de WordPress está por debajo de 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 abr 2019 16:35:39
0

Utilizo WordPress versión 5.4 con un estilo de código OOP, no sé si esto afecta por qué ninguna de las soluciones anteriores funcionó para mí, así que llegué a esta solución:

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, '[TU_SHORTCODE]'),true));

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

Espero que esto ayude a alguien.

14 abr 2020 18:02:52