Chargement conditionnel de JavaScript / CSS pour les codes courts

37

J'ai publié un plugin qui crée un shortcode et nécessite un fichier JavaScript et un fichier CSS à charger sur toute page contenant ce shortcode. Je pourrais simplement faire en sorte que le script / style soit chargé sur toutes les pages, mais ce n'est pas la meilleure pratique. Je veux seulement charger les fichiers sur les pages qui appellent le shortcode. J'ai trouvé deux méthodes pour le faire, mais les deux ont des problèmes.

La méthode 1 attribue la valeur true à un indicateur dans la fonction de gestionnaire de shortcode, puis vérifie cette valeur dans un wp_footerrappel. Si c'est vrai, il utilise wp_print_scripts()pour charger le JavaScript. Le problème, c'est que cela ne fonctionne que pour JavaScript et non pour CSS, car CSS doit être déclaré à l'intérieur <head>, ce que vous ne pouvez faire que très tôt, comme initou wp_head.

La méthode 2 se déclenche tôt et "regarde en avant" pour voir si le shortcode existe dans le contenu de la page en cours. J'aime cette méthode beaucoup mieux que la première, mais le problème qu'elle pose ne détecte pas si le modèle appelle do_shortcode().

Donc, je suis plutôt enclin à utiliser la deuxième méthode, puis à essayer de détecter si un modèle est attribué et, le cas échéant, à l’analyser pour le shortcode. Avant de faire cela, cependant, je voulais vérifier si quelqu'un connaissait une meilleure méthode.

Mise à jour: j'ai intégré la solution dans mon plugin. Si quelqu'un est curieux de le voir se dérouler dans un environnement live, vous pouvez le télécharger ou le parcourir .

Mise à jour 2: à partir de WordPress 3.3, il est désormais possible d' appeler wp_enqueue_script()directement dans un rappel de shortcode , et le fichier JavaScript sera appelé dans le pied de page du document. C'est techniquement possible pour les fichiers CSS également, mais cela devrait être considéré comme une mauvaise pratique, car la sortie de CSS en dehors de la <head>balise enfreint les spécifications du W3C, peut provoquer la FOUC et obliger le navigateur à restituer la page.

Ian Dunn
la source
J'ai déjà utilisé une variante de la méthode 1. Chargez JS pour le shortcode dans le pied de page, chargez CSS pour le shortcode en ligne. Cela fonctionne, mais c'est du bidouillage. Dans l'attente d'une meilleure solution.
EAMann
Le principal problème des CSS en ligne est qu’il est difficile de les remplacer par d’autres styles et qu’il n’utilise pas la méthode wp_enqueue_styles ().
Ian Dunn
Combien de css avez-vous?
Mfields
Combien de lignes de javascript?
Mfields
2
Le javascript est la partie facile. Le meilleur wat pour le faire est sous "Jedi" ici: scribu.net/wordpress/optimal-script-loading.html
mfields

Réponses:

11

Sur la base de ma propre expérience, j’ai utilisé une combinaison des méthodes 1 et 2, l’architecture et les scripts de pied de page de 1, et la technique de prévision 2.

Pour l’avenir, j’utilise regex à la place de stripos; préférence personnelle, plus rapidement, et peut vérifier pour shortcode «malformé»;

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

Si vous craignez que les auteurs utilisent do_shortcodemanuellement, je choisirais de leur demander d' utiliser un appel d'action pour mettre en file d' attente manuellement votre style préenregistré.

UPDATE : Pour l'auteur paresseux qui n'a jamais RTFM, affichez un message pour mettre en évidence l' erreur de leur manière;)

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
la source
C'est un bon point. Idéalement, j'aimerais que cela fonctionne sans qu'ils aient à faire quoi que ce soit, car ils ne liront probablement pas la FAQ en premier lieu. Ils supposeront donc qu'elle est cassée, mais je finirai peut-être par le faire. Je pourrais enregistrer les scripts sur chaque page, mais ne les mettre en file d'attente que si je détecte un shortcode. Ensuite, les utilisateurs peuvent se connecter à init et appeler les fonctions de mise en file d'attente dans des modèles spécifiques, le cas échéant, en supposant que l'exécution ne soit pas encore trop tardive. En outre, WP a get_shortcode_regex () intégré.
Ian Dunn
3
Si les utilisateurs sont experts en implémentation do_shortcode(), n'est-il pas raisonnable de supposer qu'ils sont également aptes à suivre les instructions suivantes pour la mise en file d'attente du style shortcode?
Chip Bennett
1
C'est vrai, mais cela donnera la regex pour tous les codes courts, pas seulement pour le tien;) "Je pourrais enregistrer les scripts sur chaque page" Probablement la meilleure méthode aussi! Notez qu'ils n'ont pas besoin de s'accrocher init, juste n'importe où auparavant wp_head. Pour le développeur paresseux, vérifiez à l' wp_style_is( 'my_style_handle', 'done' )intérieur de votre shortcode. Si c'est faux, imprimez une erreur visible qui leur indique quoi faire.
TheDeadMedic
@Chip - Je ne crains pas qu'ils ne soient pas en mesure de suivre les instructions, mais simplement qu'ils ne sachent pas qu'ils sont censés le faire, car 99% du temps, vous n'avez rien à faire.
Ian Dunn
1
@Ian, je pensais que l'ajout do_shortcode()au modèle est déjà "quelque chose de plus" - et que les utilisateurs qui feraient quelque chose de plus sauraient déjà qu'il est nécessaire de mettre le style en file d'attente ou seraient plus disposés / susceptibles de le faire. suivre des instructions spéciales.
Chip Bennett
8

Je réponds tardivement à cette question, mais depuis que Ian a commencé ce fil de discussion sur la liste des wp-hackers aujourd'hui, cela m'a fait penser que cela valait la peine de répondre, d'autant plus que j'ai prévu d'ajouter une telle fonctionnalité aux plugins sur lesquels je travaille.

Une approche à considérer consiste à vérifier sur le chargement de la première page pour voir si le shortcode est réellement utilisé, puis enregistrez le statut d'utilisation du shortcode dans une clé méta de publication. Voici comment:

Comment faire étape par étape

  1. Définir un $shortcode_useddrapeau pour 'no'.
  2. Dans la fonction shortcode, réglez l' $shortcode_usedindicateur sur 'yes'.
  3. Définissez une 'the_content'priorité de raccordement 12après que WordPress a traité les codes courts et vérifiez la méta-publication pour une ''utilisation de la clé "_has_{$shortcode_name}_shortcode". (Une valeur de ''est renvoyée lorsqu'une clé méta de publication n'existe pas pour l'ID de publication.)
  4. Utilisez un 'save_post'crochet pour supprimer la publication meta en effaçant l'indicateur de persistance pour cette publication au cas où l'utilisateur modifierait l'utilisation du code court.
  5. Également dans le 'save_post'hook, utilisez wp_remote_request()pour envoyer un HTTP GET non bloquant à la permalien de la publication afin de déclencher le chargement de la première page et le réglage de l'indicateur de persistance.
  6. Enfin, définissez a 'wp_print_styles'et vérifiez post meta pour une valeur de 'yes', 'no'ou ''utilisez la clé "_has_{$shortcode_name}_shortcode". Si la valeur est 'no'ne pas servir l'externe. Si la valeur est 'yes'ou ''aller de l'avant et servir l'externe.

Et cela devrait le faire. J'ai écrit et testé un exemple de plugin pour montrer comment tout cela fonctionne.

Exemple de code de plugin

Le plugin se réveille sur un [trigger-css]shortcode qui définit les <h2>éléments de la page en blanc sur rouge afin que vous puissiez facilement la voir fonctionner. Il suppose un csssous - répertoire contenant un style.cssfichier contenant ce CSS:

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

Et ci-dessous est le code dans un plugin de travail:

<?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();

Exemple de captures d'écran

Voici une série de captures d'écran

Post Editor basique, sans contenu

Affichage post, pas de contenu

Post Editor [trigger-css]basique avec Shortcode

Affichage de poste avec [trigger-css]Shortcode

Pas sûr si c'est 100%

Je crois que ce qui précède devrait fonctionner dans presque tous les cas, mais comme je viens d'écrire ce code, je ne peux pas être sûr à 100%. Si vous pouvez trouver des situations où cela ne fonctionne pas, j'aimerais vraiment savoir si je peux corriger le code dans certains plugins auxquels j'ai ajouté ceci. Merci d'avance.

Mike Schinkel
la source
Donc, cinq plugins utilisant votre approche vont déclencher cinq requêtes à distance chaque fois qu'une publication est enregistrée? Je préfère utiliser une regex sur post_content. Et qu'en est-il des codes courts dans les widgets?
fuxia
@toscho Le déclenchement du post-chargement est en fait facultatif; c'est seulement là pour s'assurer qu'un utilisateur n'a pas à voir la première page chargée avec les externes chargées. C'est aussi un appel non bloquant, donc en théorie, vous ne devriez pas le remarquer. Pour notre code, nous travaillons dans une classe de base afin que la classe de base ne puisse le faire qu'une seule fois. Nous pourrions raccrocher 'pre_http_request'et désactiver plusieurs appels vers la même URL lorsque le 'save_post'raccordement est actif, mais je voudrais attendre que w en voie vraiment le besoin, non? Quant aux widgets, ils pourraient être améliorés, mais ce n’est pas un cas d’utilisation que j’ai déjà examiné.
MikeSchinkel
1
@toscho - De plus, vous ne pouvez pas être sûr que le shortcode est là, car un autre crochet peut l'effacer. La seule façon dont vous pouvez être sûr est de savoir si la fonction de shortcode se déclenche. L'approche regex n'est donc pas fiable à 100%.
MikeSchinkel
Je sais. Il n'y a pas de moyen infaillible d'injecter CSS pour les codes courts (à l'aide de <style>).
fuxia
Je travaille avec une implémentation basée sur cette solution et je voulais juste souligner que cette méthode ne mettra pas en file d'attente pour un aperçu post / page.
mor7ifer
5

Googling m'a trouvé une réponse potentielle . Je dis "potentiel", car cela a l'air bien et devrait fonctionner, mais je ne suis pas convaincu à 100% que c'est la meilleure façon de le faire:

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;
    }
}

Cela devrait pouvoir vérifier si la publication actuelle utilise un shortcode et ajouter une feuille de style à l' <head>élément de manière appropriée. Mais je ne pense pas que cela fonctionnera pour une page d'index (c.-à-d. Plusieurs publications dans la boucle) ... Elle provient également d'un article de blog vieux de 2 ans. Je ne suis même pas sûr que cela fonctionne avec WP 3.1.X. .

EAMann
la source
Cela ne fonctionnera pas si le shortcode a des arguments. Si vous voulez vraiment aller dans cette voie, qui est lente, utilisez WP get_shortcode_regex()pour la recherche.
Onetrickpony
Comme je l'ai dit, réponse "potentielle" que je n'ai pas encore testée ... :-)
EAMann
C'est fondamentalement la même chose que la méthode 2, mais elle ne vérifie toujours pas le modèle pour les appels à do_shortcode ().
Ian Dunn
Pourquoi aurait-il besoin de faire ça? Si vous appelez do_shortcode()manuellement dans le modèle, vous savez déjà que vous exécuterez le shortcode
onetrickpony le
Je ne suis pas celui qui appelle le shortcode, c'est l'utilisateur. Ceci est pour un plugin distribué, pas un privé.
Ian Dunn
2

En utilisant une combinaison de la réponse de TheDeadMedic et de la documentation get_shortcode_regex () (qui n'a en fait pas trouvé mes codes abrégés), j'ai créé une fonction simple permettant de mettre en file d'attente les scripts de plusieurs codes abrégés. Comme wp_enqueue_script () dans les codes abrégés ne fait qu'ajouter un pied de page, cela peut être utile car il peut gérer les scripts d'en-tête et de pied de page.


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
la source
1

Enfin, j’ai également trouvé une solution de chargement css conditionnel qui fonctionne pour mon plugin www.mapsmarker.com et que j’aimerais partager avec vous. Il vérifie si mon shortcode est utilisé dans le fichier de modèle actuel et dans l'en-tête / footer.php et si c'est le cas, met la feuille de style nécessaire dans l'en-tête:

  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
la source
Un peu de côté, mais cela ne suppose-t-il pas que les gens utilisent header.php et footer.php. Qu'en est-il des méthodes d'habillage de thèmes telles que celles décrites par scribu.net/wordpress/theme-wrappers.html ? ou des thèmes comme les racines qui gardent leurs gabarits ailleurs?
Orionrush
1

Pour mon plugin, j'ai constaté que parfois les utilisateurs ont un générateur de thème qui a un shortcode stocké dans des méta-données post . Voici ce que j'utilise pour détecter si mon shortcode de plugin est présent dans les métadonnées post ou post actuelles :

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
la source
0

parce que CSS doit être déclaré à l'intérieur <head>

Pour les fichiers CSS, vous pouvez les charger dans la sortie de votre shortcode:

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

Définissez une constante ou quelque chose après cela, comme MY_CSS_LOADED (inclure uniquement le CSS si la constante n'est pas définie).

Vos deux méthodes sont plus lentes que d'aller de cette façon.

Pour les fichiers JS, vous pouvez faire de même si le script que vous chargez est unique et ne comporte aucune dépendance extérieure. Si ce n'est pas le cas, chargez-le à l'intérieur du pied de page, mais utilisez la constante pour déterminer si vous devez le charger ou non ...

onetrickpony
la source
3
Le chargement de CSS en dehors de l' <head>élément n'est pas un balisage approprié. Certes, la validation n’est qu’une directive, mais si nous essayons de nous en tenir à cette directive, le chargement de la feuille de style dans la sortie du code court est une mauvaise idée.
EAMann
Les blocs CSS en ligne sont des balises valides, même en XHTML, à ma connaissance. Il n'y a aucune raison de ne pas les utiliser quand vous n'avez pas d'autre alternative acceptable
onetrickpony le
1
Selon l'outil de validation du 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". Les styles en ligne ( <element style="..."></element>) sont donc valables, mais les <style>éléments en ligne ne le sont pas.
EAMann
1
rendez-le filtrable et n'importe quel autre plugin ou thème peut faire ce qu'il veut. S'ils configurent le filtre pour renvoyer une chaîne vide, rien ne sera imprimé.
Mfields
1
Vous n'avez mentionné aucune raison objective contre cette pratique. En tout cas ça ne fait rien; Je ne vois que deux options ici: toujours charger les CSS / scripts (les optimiser pour la taille), ou les styles en ligne conditionnels
onetrickpony le
0

Script Logic est un plugin WordPress qui vous donne un contrôle total sur tous les fichiers JavaScript et CSS. En utilisant ce plugin, vous pouvez conditionnellement charger le fichier CSS et JS uniquement sur les pages où cela est nécessaire.

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

Tahir Yasin
la source
Ce n'est pas vraiment pertinent pour cette question, qui concerne le contrôle de la mise en file d'attente à partir du plugin lui-même. Votre approche nécessiterait de demander à l'utilisateur d'installer un autre plugin, puis de comprendre comment le configurer correctement.
Ian Dunn