Carga condicional de JavaScript / CSS para códigos cortos

37

Lancé un complemento que crea un código corto y requiere un archivo JavaScript y un archivo CSS para cargar en cualquier página que contenga ese código corto. Podría hacer que el script / estilo se cargue en todas las páginas, pero esa no es la mejor práctica. Solo quiero cargar los archivos en páginas que llaman al shortcode. He encontrado dos métodos para hacer esto, pero ambos tienen problemas.

El método 1 establece un indicador en verdadero dentro de la función del controlador de shortcode y luego verifica ese valor dentro de una wp_footerdevolución de llamada. Si es cierto, se usa wp_print_scripts()para cargar el JavaScript. El problema con esto es que solo funciona para JavaScript y no CSS, porque CSS debe declararse dentro <head>, lo que solo puede hacer durante un gancho temprano como inito wp_head.

El método 2 se dispara temprano y "mira hacia adelante" para ver si el shortcode existe en el contenido de la página actual. Me gusta este método mucho mejor que el primero, pero el problema con él no detectará si la plantilla llama do_shortcode().

Por lo tanto, me estoy inclinando hacia el uso del segundo método y luego trato de detectar si se asigna una plantilla y, de ser así, analizarla para el shortcode. Sin embargo, antes de hacer eso, quería comprobar si alguien sabe de un método mejor.

Actualización: he integrado la solución en mi complemento. Si alguien tiene curiosidad por verlo desarrollado en un entorno en vivo, puede descargarlo o navegarlo .

Actualización 2: a partir de WordPress 3.3, ahora es posible llamar wp_enqueue_script()directamente dentro de una devolución de llamada de código corto , y el archivo JavaScript se llamará dentro del pie de página del documento. Eso también es técnicamente posible para los archivos CSS, pero debe considerarse una mala práctica porque la salida de CSS fuera de la <head>etiqueta viola las especificaciones del W3C, puede encerrar FOUC y puede forzar al navegador a volver a representar la página.

Ian Dunn
fuente
He usado una variación del Método 1 en el pasado. Cargue JS para el shortcode en el pie de página, cargue CSS para el shortcode en línea. Funciona, pero es hackish. Esperamos una mejor solución.
EAMann
Los principales problemas con CSS en línea son que es difícil anular con otros estilos, y no utiliza el método wp_enqueue_styles ().
Ian Dunn
¿Cuánto css tienes?
mfields
¿Cuántas líneas de javascript?
mfields
2
El javascript es la parte fácil. La mejor manera de hacerlo es en "Jedi" aquí: scribu.net/wordpress/optimal-script-loading.html
mfields

Respuestas:

11

Según mi propia experiencia, he usado una combinación de los métodos 1 y 2: la arquitectura y los scripts de pie de página de 1, y la técnica de "anticipación" de 2.

Sin embargo, para el futuro, uso regex en lugar de stripos; preferencia personal, más rápido y puede verificar el shortcode 'malformado';

preg_match( '#\[ *shortcode([^\]])*\]#i', $content );

Si le preocupa que los autores lo usen do_shortcodemanualmente, optaría por indicarles que usen una llamada a la acción para poner en cola su estilo previamente registrado manualmente.

ACTUALIZACIÓN : Para el autor perezoso que nunca RTFM, envía un mensaje para resaltar el error de sus formas;)

function my_shortcode()
{
    static $enqueued;
    if ( ! isset( $enqueued ) )
        $enqueued = wp_style_is( 'my_style', 'done' ); // cache it so we don't repeat if called over and over

    // do shortcode
    $output = '';

    if ( ! $enqueued )
        // you can output the message on first occurence only by wrapping it in the previous if
        $output .= <<<HTML
<p>Attention! You must enqueue the shortcode stylesheet yourself if calling <code>do_shortcode()</code> directly!</p>
<p>Use <code>wp_enqueue_style( 'my_style' );</code> before your <code>get_header()</code> call inside your template.</p>
HTML;

    return $output;
}
TheDeadMedic
fuente
Ese es un buen punto. Idealmente, me gustaría que funcionase sin que tuvieran que hacer nada extra, porque la mitad del tiempo probablemente no leerán las preguntas frecuentes primero, por lo que simplemente asumirán que está roto, pero podría terminar haciendo eso. Podría registrar los scripts en cada página, pero solo los pondré en cola si detecto un código corto. Luego, los usuarios podrían conectarse a init y llamar a las funciones en cola en plantillas específicas donde sea necesario, suponiendo que aún no sea demasiado tarde en la ejecución en ese punto. Además, WP tiene get_shortcode_regex () incorporado.
Ian Dunn
3
Si los usuarios son expertos en la implementación do_shortcode(), ¿no es razonable suponer que también son expertos en seguir las instrucciones para crear el estilo shortcode?
Chip Bennett
1
Es cierto, pero obtendrá la expresión regular para todos los códigos cortos, no solo los suyos;) "Podría registrar los scripts en cada página" ¡Probablemente el mejor método también! Tenga en cuenta que no necesitan conectarse init, solo en cualquier lugar antes wp_head. Para el desarrollador perezoso, verifique wp_style_is( 'my_style_handle', 'done' )dentro de su shortcode. Si es falso, imprima un error visible que les indique qué hacer.
TheDeadMedic
@Chip: no me preocupa que no sean capaces de seguir las instrucciones, solo que no sabrán que se supone que deben hacerlo, ya que el 99% del tiempo no necesita hacer nada adicional.
Ian Dunn
1
@Ian, pensé que agregar do_shortcode()a la plantilla ya era "hacer [algo] extra", y los usuarios que harían algo extra ya sabrían sobre la necesidad de crear el estilo, o de lo contrario estarían más dispuestos / probablemente seguir instrucciones especiales.
Chip Bennett
8

Llegué tarde a responder esta pregunta, pero desde que Ian comenzó este hilo en la lista de wp-hackers hoy, me hizo pensar que valía la pena responder, especialmente teniendo en cuenta que he estado planeando agregar esa característica a algunos complementos en los que he estado trabajando.

Un enfoque a considerar es verificar la carga de la primera página para ver si el shortcode se usa realmente y luego guardar el estado de uso del shortcode en una meta clave. Así es cómo:

Instrucciones paso a paso

  1. Establecer una $shortcode_usedbandera para 'no'.
  2. En la función de shortcode, establezca la $shortcode_usedbandera en 'yes'.
  3. Establezca una 'the_content'prioridad de enlace 12que es después de que WordPress haya procesado los códigos cortos y verifique la meta del mensaje para ''usar la clave "_has_{$shortcode_name}_shortcode". (Se ''devuelve un valor de cuando no existe una metaclave de publicación para la ID de publicación).
  4. Use un 'save_post'gancho para eliminar la metaetiqueta de la publicación, borrando el indicador persistente para esa publicación en caso de que el usuario cambie el uso de shortcode.
  5. También en el 'save_post'gancho, use wp_remote_request()para enviar un HTTP GET sin bloqueo al enlace permanente de la publicación para activar la carga de la primera página y la configuración del indicador persistente.
  6. Por último, establezca 'wp_print_styles'y verifique el meta meta de la publicación para obtener un valor de 'yes', 'no'o ''utilizando la clave "_has_{$shortcode_name}_shortcode". Si el valor es 'no'no sirva al externo. Si el valor es 'yes'o ''sigue adelante y sirve al externo.

Y eso debería hacerlo. He escrito y probado un complemento de ejemplo para mostrar cómo funciona todo esto.

Código de complemento de ejemplo

El complemento se activa en un [trigger-css]código corto que establece los <h2>elementos de la página en blanco sobre rojo para que pueda verlo funcionar fácilmente. Asume un csssubdirectorio que contiene un style.cssarchivo con este CSS:

/*
 * Filename: css/style.css
 */
h2 {
  color: white;
  background: red;
}

Y a continuación se muestra el código en un complemento que funciona:

<?php
/**
 * Plugin Name: CSS on Shortcode
 * Description: Shows how to conditionally load a shortcode
 * Author: Mike Schinkel <[email protected]>
 */
class CSS_On_Shortcode {

  /**
   * @var CSS_On_Shortcode
   */
  private static $_this;

  /**
   * @var string 'yes'/'no' vs. true/false as get_post_meta() returns '' for false and not found.
   */
  var $shortcode_used = 'no';

  /**
   * @var string
   */
  var $HAS_SHORTCODE_KEY = '_has_trigger-css_shortcode';
  /**
   *
   */
  function __construct() {
    self::$_this = $this;
    add_shortcode( 'trigger-css', array( $this, 'do_shortcode' ) );
    add_filter( 'the_content', array( $this, 'the_content' ), 12 ); // AFTER WordPress' do_shortcode()
    add_action( 'save_post', array( $this, 'save_post' ) );
    add_action( 'wp_print_styles', array( $this, 'wp_print_styles' ) );
  }

  /**
   * @return CSS_On_Shortcode
   */
  function this() {
    return self::$_this;
  }

  /**
   * @param array $arguments
   * @param string $content
   * @return string
   */
  function do_shortcode( $arguments, $content ) {
    /**
     * If this shortcode is being used, capture the value so we can save to post_meta in the 'the_content' filter.
     */
    $this->shortcode_used = 'yes';
    return '<h2>THIS POST WILL ADD CSS TO MAKE H2 TAGS WHITE ON RED</h2>';
  }

  /**
   * Delete the 'has_shortcode' meta value so that it can be regenerated
   * on first page load in case shortcode use has changed.
   *
   * @param int $post_id
   */
  function save_post( $post_id ) {
    delete_post_meta( $post_id, $this->HAS_SHORTCODE_KEY );
    /**
     * Now load the post asynchronously via HTTP to pre-set the meta value for $this->HAS_SHORTCODE_KEY.
     */
    wp_remote_request( get_permalink( $post_id ), array( 'blocking' => false ) );
  }

  /**
   * @param array $args
   *
   * @return array
   */
  function wp_print_styles( $args ) {
    global $post;
    if ( 'no' != get_post_meta( $post->ID, $this->HAS_SHORTCODE_KEY, true ) ) {
      /**
       * Only bypass if set to 'no' as '' is unknown.
       */
      wp_enqueue_style( 'css-on-shortcode', plugins_url( 'css/style.css', __FILE__ ) );
    }
   }

  /**
   * @param string $content
   * @return string
   */
  function the_content( $content ) {
    global $post;
    if ( '' === get_post_meta( $post->ID, $this->HAS_SHORTCODE_KEY, true ) ) {
      /**
       * This is the first time the shortcode has ever been seen for this post.
       * Save a post_meta key so that next time we'll know this post uses this shortcode
       */
      update_post_meta( $post->ID, $this->HAS_SHORTCODE_KEY, $this->shortcode_used );
    }
    /**
     * Remove this filter now. We don't need it for this post again.
     */
    remove_filter( 'the_content', array( $this, 'the_content' ), 12 );
    return $content;
  }

}
new CSS_On_Shortcode();

Capturas de pantalla de ejemplo

Aquí hay una serie de capturas de pantalla

Editor de publicaciones básico, sin contenido

Visualización de publicaciones, sin contenido

Editor de publicaciones básico con [trigger-css]shortcode

Visualización de publicaciones con [trigger-css]código corto

No estoy seguro si es 100%

Creo que lo anterior debería funcionar en casi todos los casos, pero como acabo de escribir este código, no puedo estar 100% seguro. Si puede encontrar situaciones en las que no funciona, me gustaría saberlo para poder arreglar el código en algunos complementos a los que acabo de agregar esto. Gracias por adelantado.

MikeSchinkel
fuente
¿Entonces cinco complementos que utilizan su enfoque activarán cinco solicitudes remotas cada vez que se guarde una publicación? Prefiero usar una expresión regular post_content. ¿Y qué hay de los códigos cortos en widgets?
fuxia
@toscho La activación de la carga posterior es realmente opcional; solo está ahí para garantizar que un usuario no tenga que ver la carga de la primera página con los elementos externos cargados. También es una llamada sin bloqueo, por lo que, en teoría, no debería notarlo. Para nuestro código lo estamos haciendo en una clase base, por lo que la clase base puede manejar hacerlo solo una vez. Podríamos enganchar el 'pre_http_request'gancho y deshabilitar múltiples llamadas a la misma URL mientras el 'save_post'enganche está activo, pero me gustaría esperar hasta que realmente veamos la necesidad de eso, ¿no? En cuanto a los widgets, podría ser mejorado para manejar, pero no es un caso de uso que he visto todavía.
MikeSchinkel
1
@toscho: tampoco puede estar seguro de que el shortcode esté allí ya que otro gancho puede borrarlo. La única forma en que puede estar seguro es si la función de shortcode realmente se activa. Por lo tanto, el enfoque regex no es 100% confiable.
MikeSchinkel
Lo sé. No hay una forma a prueba de balas para inyectar CSS para códigos cortos (excepto el uso <style>).
fuxia
Estoy trabajando con una implementación basada en esta solución y solo quería señalar que este método no generará una vista previa de la publicación / página.
mor7ifer
5

Buscar en Google me encontró una respuesta potencial . Digo "potencial" porque se ve bien, debería funcionar, pero no estoy 100% convencido de que sea la mejor manera de hacerlo:

add_action( 'wp_print_styles', 'yourplugin_include_css' );
function yourplugin_include_css() {
    // Check if shortcode exists in page or post content
    global $post;

    // I removed the end ' ] '... so it can accept args.
    if ( strstr( $post->post_content, '[yourshortcode ' ) ) {
        echo $csslink;
    }
}

Esto debería poder verificar si la publicación actual está usando un código corto y agregar una hoja de estilo al <head>elemento adecuadamente. Pero no creo que funcione para una página de índice (es decir, varias publicaciones en el bucle) ... También es de una publicación de blog de 2 años de antigüedad, por lo que ni siquiera estoy seguro de que funcione con WP 3.1.X .

EAMann
fuente
Esto no funcionará si el shortcode tiene argumentos. Si realmente quiere ir por este camino, que es lento, use los WP get_shortcode_regex()para buscar.
onetrickpony
Como dije, "potencial" respuesta que aún no he probado ... :-)
EAMann
Eso es básicamente lo mismo que el método 2, pero aún no verifica la plantilla para las llamadas do_shortcode ().
Ian Dunn
¿Por qué necesitaría hacer eso? Si llama do_shortcode()manualmente en la plantilla, entonces ya sabe que ejecutará el shortcode
onetrickpony
No soy yo quien llama al shortcode, es el usuario. Esto es para un complemento distribuido, no privado.
Ian Dunn
2

Usando una combinación de la respuesta de TheDeadMedic y la documentación de get_shortcode_regex () (que en realidad no encontró mis códigos cortos), creé una función simple utilizada para poner en secuencia los scripts para múltiples códigos cortos. Dado que wp_enqueue_script () en códigos cortos solo se agrega al pie de página, esto puede ser útil ya que puede manejar tanto los scripts de encabezado como de pie de página.


function add_shortcode_scripts() {
    global $wp_query;   
    $posts = $wp_query->posts;
    $scripts = array(
        array(
            'handle' => 'map',
            'src' => 'http://maps.googleapis.com/maps/api/js?sensor=false',
            'deps' => '',
            'ver' => '3.0',
            'footer' => false
        ),
        array(
            'handle' => 'contact-form',
            'src' => get_template_directory_uri() . '/library/js/jquery.validate.min.js',
            'deps' => array( 'jquery' ),
            'ver' => '1.11.1',
            'footer' => true
        )   
    );

    foreach ( $posts as $post ) {
        foreach ( $scripts as $script ) {
            if ( preg_match( '#\[ *' . $script['handle'] . '([^\]])*\]#i', $post->post_content ) ) {
                // enqueue css and/or js
                if ( wp_script_is( $script['handle'], 'registered' ) ) {
                    return;
                } else {
                    wp_register_script( $script['handle'], $script['src'], $script['deps'], $script['ver'], $script['footer'] );
                    wp_enqueue_script( $script['handle'] );
                }
            }
        }
    }
}
add_action( 'wp', 'add_shortcode_scripts' );
Sean Michaud
fuente
1

Finalmente, también encontré una solución para la carga condicional de CSS que funciona para mi plugin www.mapsmarker.com y me gustaría compartirla con ustedes. Comprueba si mi shortcode se usa dentro del archivo de plantilla actual y header / footer.php y, en caso afirmativo, pone en cola la hoja de estilo necesaria en el encabezado:

  function prefix_template_check_shortcode( $template ) {
    $searchterm = '[mapsmarker';
    $files = array( $template, get_stylesheet_directory() . DIRECTORY_SEPARATOR . 'header.php', get_stylesheet_directory() . DIRECTORY_SEPARATOR . 'footer.php' );
    foreach( $files as $file ) {
        if( file_exists($file) ) {
            $contents = file_get_contents($file);
            if( strpos( $contents, $searchterm )  ) {
                wp_enqueue_style('
leafletmapsmarker', LEAFLET_PLUGIN_URL . 'leaflet-dist/leaflet.css');
                  break; 
            }
        }
    }
  return $template;
  }  
  add_action('template_include','prefix_template_check_shortcode' );
robertharm
fuente
un poco aparte, pero esto no supone que las personas están usando header.php y footer.php. ¿Qué pasa con los métodos de ajuste de temas como los descritos por scribu.net/wordpress/theme-wrappers.html ? o temas como raíces que mantienen sus partes de plantilla en otro lugar?
orionrush
1

Para mi complemento, descubrí que a veces los usuarios tienen un generador de temas que tiene un código abreviado almacenado en metadatos posteriores . Esto es lo que estoy usando para detectar si mi código abreviado del complemento está presente en la publicación actual o en los metadatos de la publicación :

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

       // determine whether this page contains "my_shortcode" 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' );
zdenekca
fuente
0

porque CSS debe declararse dentro <head>

Para archivos CSS, puede cargarlos dentro de su salida de código corto:

<style type="text/css">
  @import "path/to/your.css"; 
</style>

Establezca una constante o algo después de esto, como MY_CSS_LOADED(solo incluya el CSS si la constante no está configurada).

Ambos métodos son más lentos que ir de esta manera.

Para los archivos JS, puede hacer lo mismo si el script que está cargando es único y no tiene dependencias externas. Si este no es el caso, cárguelo dentro del pie de página, pero use la constante para determinar si necesita cargarse o no ...

Pony de un solo truco
fuente
3
Cargar CSS fuera del <head>elemento no es un marcado adecuado. Es cierto que la validación es solo una guía, pero si estamos tratando de cumplir con esa guía, es una mala idea cargar la hoja de estilo dentro de la salida del código corto.
EAMann
Los bloques CSS en línea son marcas válidas, incluso en XHTML por lo que recuerdo. No hay razón para no usarlos cuando no tiene otra alternativa aceptable
onetrickpony
1
De acuerdo con herramienta de validación del W3C: <style type="text/css"> The element named above was found in a context where it is not allowed. This could mean that you have incorrectly nested elements -- such as a "style" element in the "body" section instead of inside "head". Por lo tanto, los estilos en línea ( <element style="..."></element>) son válidos, pero los <style>elementos en línea no lo son.
EAMann
1
hágalo filtrable y cualquier otro complemento o tema puede hacer lo que quiera con él. Si configuran el filtro para que devuelva una cadena vacía, no se imprimirá nada.
mfields
1
No mencionaste ninguna razón objetiva contra esta práctica. De todos modos no importa; Aquí solo veo dos opciones: cargar siempre CSS / scripts (optimizarlos para el tamaño) o estilos en línea condicionales
onetrickpony
0

Script Logic es un complemento de WordPress que le brinda control total sobre todos los archivos JavaScript y CSS. Con este complemento, puede cargar condicionalmente archivos CSS y JS solo en las páginas donde sea necesario.

http://wordpress.org/plugins/script-logic/

Tahir Yasin
fuente
Eso no es realmente relevante para esta pregunta, que se trata de controlar la puesta en cola desde el propio complemento. Su enfoque requeriría pedirle al usuario que instale otro complemento y luego comprender cómo configurarlo correctamente.
Ian Dunn