Caricamento condizionale di JavaScript / CSS per codici brevi


37

Ho rilasciato un plug-in che crea uno shortcode e richiede un file JavaScript e un file CSS da caricare su qualsiasi pagina che contiene tale shortcode. Potrei solo caricare lo script / stile su tutte le pagine, ma non è la migliore pratica. Voglio solo caricare i file su pagine che chiamano lo shortcode. Ho trovato due metodi per farlo, ma entrambi hanno problemi.

Il metodo 1 imposta un flag su true all'interno della funzione del gestore shortcode e quindi controlla quel valore all'interno di un wp_footercallback. Se è vero, utilizza wp_print_scripts()per caricare JavaScript. Il problema è che funziona solo per JavaScript e non CSS, perché i CSS dovrebbero essere dichiarati all'interno <head>, cosa che puoi fare solo durante un hook iniziale come inito wp_head.

Il metodo 2 viene avviato in anticipo e "anticipa" per vedere se lo shortcode esiste nel contenuto della pagina corrente. Mi piace questo metodo molto meglio del primo, ma il problema con esso non rileverà se il modello chiama do_shortcode().

Quindi, mi sto orientando verso l'utilizzo del secondo metodo e quindi sto cercando di rilevare se un modello è assegnato e, in tal caso, analizzarlo per lo shortcode. Prima di farlo, però, volevo verificare se qualcuno conosce un metodo migliore.

Aggiornamento: ho integrato la soluzione nel mio plugin. Se qualcuno è curioso di vederlo arricchito in un ambiente live, puoi scaricarlo o sfogliarlo .

Aggiornamento 2: A partire da WordPress 3.3, ora è possibile chiamare wp_enqueue_script()direttamente all'interno di un callback di shortcode e il file JavaScript verrà chiamato nel piè di pagina del documento. Ciò è tecnicamente possibile anche per i file CSS, ma dovrebbe essere considerato una cattiva pratica perché l'output di CSS al di fuori del <head>tag viola le specifiche W3C, può caso FOUC e può costringere il browser a eseguire nuovamente il rendering della pagina.


Ho usato una variante del metodo 1 in passato. Carica JS per lo shortcode nel piè di pagina, carica CSS per lo shortcode in linea. Funziona, ma è hacker. In attesa di una soluzione migliore.
EAMann,

I problemi principali con CSS inline sono che è difficile sovrascrivere con altri stili e non usa il metodo wp_enqueue_styles ().
Ian Dunn,

Quanto css hai?
Mfields,

Quante linee di javascript?
Mfields,

2
Il javascript è la parte facile. Il miglior wat per fare questo è sotto "Jedi" qui: scribu.net/wordpress/optimal-script-loading.html
mfields

Risposte:


11

Sulla base della mia esperienza, ho usato una combinazione di metodo 1 e 2: gli script di architettura e piè di pagina di 1 e la tecnica "look-ahead" di 2.

Per il futuro però, uso regex al posto di stripos; preferenza personale, più veloce e in grado di verificare la presenza di shortcode "non validi";

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

Se sei preoccupato per gli autori che usano do_shortcodemanualmente, sceglierei di istruirli a usare una call action accodando manualmente il tuo stile pre-registrato.

AGGIORNAMENTO : per l'autore pigro che non ha mai RTFM, emette un messaggio per evidenziare l' errore dei propri modi;)

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

È un buon punto. Idealmente, mi piacerebbe che funzionasse senza che debbano fare qualcosa in più - perché per metà delle volte probabilmente non leggeranno prima le FAQ, quindi presumeranno che sia rotto - ma potrei finire per farlo. Potrei registrare gli script su ogni pagina, ma accodarli solo se rilevo uno shortcode. Quindi, gli utenti potrebbero agganciarsi a init e chiamare le funzioni di accodamento in modelli specifici dove necessario, supponendo che non sia già troppo tardi nell'esecuzione a quel punto. Inoltre, WP ha get_shortcode_regex () integrato.
Ian Dunn,

3
Se gli utenti sono abili nell'implementazione do_shortcode(), non è ragionevole supporre che siano anche abili nel seguire le istruzioni per accodare lo stile di shortcode?
Chip Bennett,

1
È vero, ma otterrà il regex per tutti i codici brevi, non solo i tuoi;) "Potrei registrare gli script su ogni pagina" Probabilmente anche il metodo migliore! Nota che non hanno bisogno di agganciarsi init, solo ovunque prima wp_head. Per lo sviluppatore pigri, controllare wp_style_is( 'my_style_handle', 'done' )all'interno della vostra shortcode. Se è falso, stampa un errore visibile che indica loro cosa fare.
TheDeadMedic il

@Chip - Non sono preoccupato che non siano in grado di seguire le istruzioni, solo che non sapranno che dovrebbero, dato che il 99% delle volte non è necessario fare altro.
Ian Dunn,

1
@Ian il mio pensiero era che l'aggiunta do_shortcode()al modello è già "fare [ing] qualcosa in più" - e gli utenti che farebbero quel qualcosa in più sarebbero già a conoscenza della necessità di accodare lo stile, oppure sarebbero più disposti / probabili seguire le istruzioni speciali.
Chip Bennett,

8

Sto rispondendo in ritardo a questa domanda, ma da quando Ian ha iniziato questa discussione nella lista degli hacker di wp oggi mi ha fatto pensare che valesse la pena rispondere soprattutto considerando che ho intenzione di aggiungere una tale funzionalità ad alcuni plugin su cui sto lavorando.

Un approccio da considerare è quello di verificare il caricamento della prima pagina per vedere se lo shortcode è effettivamente utilizzato e quindi salvare lo stato di utilizzo dello shortcode in una meta meta post. Ecco come:

Procedura dettagliata

  1. Imposta una $shortcode_usedbandiera su 'no'.
  2. Nella funzione shortcode stessa imposta il $shortcode_usedflag su 'yes'.
  3. Imposta una 'the_content'priorità hook 12che è dopo che WordPress ha elaborato i codici brevi e controlla la meta post per un ''usando il tasto "_has_{$shortcode_name}_shortcode". (Viene ''restituito un valore di quando non esiste una meta chiave post per l'ID post.)
  4. Utilizzare un 'save_post'hook per eliminare la meta meta cancellando il flag persistente per quel post nel caso in cui l'utente cambi l'utilizzo dello shortcode.
  5. Inoltre, 'save_post'usa l' hook wp_remote_request()per inviare un HTTP GET non bloccante al proprio permalink del post per attivare il caricamento della prima pagina e l'impostazione del flag persistente.
  6. Infine imposta un 'wp_print_styles'e controlla il post meta per un valore di 'yes', 'no'o ''usando la chiave "_has_{$shortcode_name}_shortcode". Se il valore è 'no'non servire l'esterno. Se il valore è 'yes'o ''vai avanti e servi l'esterno.

E così dovrebbe andare. Ho scritto e testato un plug-in di esempio per mostrare come funziona tutto questo.

Esempio di codice plug-in

Il plug-in si riattiva su uno [trigger-css]shortcode che imposta gli <h2>elementi della pagina su bianco-rosso in modo da poterlo vedere facilmente funzionante. Presuppone una csssottodirectory contenente il style.cssfile con questo CSS al suo interno:

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

E di seguito è riportato il codice in un plugin funzionante:

<?php
/**
 * Plugin Name: CSS on Shortcode
 * Description: Shows how to conditionally load a shortcode
 * Author: Mike Schinkel <mike@newclarity.net>
 */
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();

Schermate di esempio

Ecco una serie di schermate

Editor di post di base, nessun contenuto

Pubblica display, nessun contenuto

Editor di posta base con [trigger-css]shortcode

Pubblica display con [trigger-css]shortcode

Non sono sicuro se è al 100%

Credo che quanto sopra dovrebbe funzionare in quasi tutti i casi, ma dato che ho appena scritto questo codice non posso esserne sicuro al 100%. Se riesci a trovare situazioni in cui non funziona, mi piacerebbe davvero saperlo, così posso correggere il codice in alcuni plugin a cui ho appena aggiunto questo. Grazie in anticipo.


Quindi cinque plugin che usano il tuo approccio attiveranno cinque richieste remote ogni volta che un post viene salvato? Preferirei usare una regex su post_content. E i codici brevi nei widget?
fuxia

@toscho L'attivazione del post caricamento è in realtà facoltativa; è lì solo per garantire che un utente non debba vedere il caricamento della prima pagina con gli esterni caricati. È anche una chiamata non bloccante, quindi in teoria non dovresti notarlo. Per il nostro codice lo stiamo facendo in una classe base in modo che la classe base possa gestirla solo una volta. Potremmo agganciare l' 'pre_http_request'hook e disabilitare più chiamate allo stesso URL mentre l' 'save_post'hook è attivo, ma vorrei aspettare fino a quando non ne vedessimo davvero la necessità, no? Per quanto riguarda i widget, potrebbe essere migliorato da gestire, ma non è ancora un caso d'uso che ho esaminato.
MikeSchinkel,

1
@toscho - Inoltre non puoi essere sicuro che lo shortcode sia presente, poiché un altro hook potrebbe cancellarlo. L'unico modo per essere sicuri è se la funzione shortcode si attiva effettivamente. Quindi l'approccio regex non è affidabile al 100%.
MikeSchinkel,

Lo so. Non esiste un modo a prova di proiettile per iniettare CSS per codici brevi (tranne che per l'uso <style>).
fuxia

Sto lavorando con un'implementazione basata su questa soluzione e volevo solo sottolineare che questo metodo non si accoderà per un'anteprima di post / pagina.
mor7ifer,

5

Googling mi ha trovato una potenziale risposta . Dico "potenziale" in quanto sembra buono, dovrebbe funzionare, ma non sono convinto al 100% che sia il modo migliore per farlo:

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

Questo dovrebbe essere in grado di verificare se il post corrente sta usando un shortcode e aggiungere un foglio di stile <head>all'elemento in modo appropriato. Ma non credo che funzionerà per una pagina di indice (cioè più post nel ciclo) ... È anche da un vecchio post di 2 anni, quindi non sono nemmeno sicuro che funzionerà con WP 3.1.X .


Questo non funzionerà se lo shortcode ha argomenti. Se vuoi davvero andare in questo modo, che è lento, usa WP get_shortcode_regex()per la ricerca.
onetrickpony,

Come ho detto, risposta "potenziale" che non ho ancora testato ... :-)
EAMann,

Questo è fondamentalmente lo stesso del metodo 2, ma non controlla ancora il modello per le chiamate do_shortcode ().
Ian Dunn,

Perché dovrebbe farlo? Se chiami do_shortcode()manualmente nel modello, sai già che eseguirai lo shortcode
onetrickpony,

Non sono io a chiamare lo shortcode, l'utente lo è. Questo è per un plugin distribuito, non privato.
Ian Dunn,

2

Usando una combinazione della risposta di TheDeadMedic e della documentazione get_shortcode_regex () (che in realtà non ha trovato i miei codici brevi), ho creato una semplice funzione utilizzata per accodare gli script per più codici brevi. Poiché wp_enqueue_script () nei codici brevi si aggiunge solo al piè di pagina, questo può essere utile in quanto può gestire sia script di intestazione che piè di pagina.


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

1

Alla fine ho anche trovato una soluzione per il caricamento css condizionale che funziona per il mio plugin www.mapsmarker.com e mi piacerebbe condividere con te. Verifica se il mio shortcode è utilizzato nel file modello corrente e nell'intestazione / footer.php e se sì, accoda il foglio di stile necessario nell'intestazione:

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

un po 'da parte ma questo non presuppone che le persone stiano usando header.php e footer.php. Che dire dei metodi di avvolgimento dei temi come quelli descritti da scribu.net/wordpress/theme-wrappers.html ? o temi come le radici che mantengono le loro parti del modello altrove?
Orionrush,

1

Per il mio plugin ho scoperto che a volte gli utenti hanno un generatore di temi che ha shortcode memorizzati nei metadati post . Ecco cosa sto usando per rilevare se il mio shortcode plug-in è presente nel post corrente o post meta dati :

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

0

perché i CSS dovrebbero essere dichiarati all'interno <head>

Per i file CSS è possibile caricarli nell'output dello shortcode:

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

Imposta una costante o qualcosa del genere, ad esempio MY_CSS_LOADED(includi il CSS solo se la costante non è impostata).

Entrambi i tuoi metodi sono più lenti che andare in questo modo.

Per i file JS puoi fare lo stesso se lo script che stai caricando è unico e non ha dipendenze esterne. In caso contrario, caricalo nel piè di pagina, ma usa la costante per determinare se deve essere caricato o meno ...


3
Il caricamento di CSS all'esterno <head>dell'elemento non è un markup corretto. È vero, la convalida è solo una linea guida, ma se stiamo cercando di attenerci a questa linea guida, il caricamento del foglio di stile all'interno dell'output dello shortcode è una cattiva idea.
EAMann,

I blocchi CSS incorporati sono markup validi, anche in XHTML da quello che ricordo. Non c'è motivo di non usarli quando non hai altre alternative accettabili
onetrickpony,

1
Secondo strumento di validazione 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". Quindi gli stili inline ( <element style="..."></element>) sono validi, ma gli <style>elementi inline no.
EAMann,

1
renderlo filtrabile e qualsiasi altro plug-in o tema può fare come piace a lui. Se configurano il filtro per restituire una stringa vuota, non verrà stampato nulla.
Mfields,

1
Non hai menzionato ragioni obiettive contro questa pratica. Comunque non importa; Vedo solo due opzioni qui: carica sempre CSS / script (ottimizzali per dimensione) o stili in linea condizionali
onetrickpony

0

Script Logic è un plug-in di WordPress che ti offre il pieno controllo di tutti i file JavaScript e CSS. Utilizzando questo plug-in è possibile caricare in modo condizionale file CSS e JS solo su pagine dove necessario.

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


Questo non è veramente rilevante per questa domanda, che riguarda il controllo dell'accodamento all'interno del plugin stesso. Il tuo approccio richiederebbe all'utente di installare un altro plugin e quindi capire come configurarlo correttamente.
Ian Dunn,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.