Warunkowo ładuję JavaScript / CSS dla skrótów

37

Wydałem wtyczkę, która tworzy krótki kod i wymaga pliku JavaScript i pliku CSS do załadowania na dowolnej stronie zawierającej ten kod. Mógłbym po prostu załadować skrypt / styl na wszystkie strony, ale to nie jest najlepsza praktyka. Chcę tylko ładować pliki na stronach wywołujących shortcode. Znalazłem dwie metody na zrobienie tego, ale obie mają problemy.

Metoda 1 ustawia flagę na true w funkcji obsługi krótkiego kodu, a następnie sprawdza tę wartość w wp_footerwywołaniu zwrotnym. Jeśli to prawda, używa wp_print_scripts()do załadowania JavaScript. Problem polega na tym, że działa tylko dla JavaScript, a nie CSS, ponieważ CSS powinien być zadeklarowany w środku <head>, co można zrobić tylko podczas wczesnego haka, takiego jak initlub wp_head.

Metoda 2 uruchamia się wcześnie i „zagląda do przodu”, aby sprawdzić, czy w bieżącej zawartości strony występuje krótki kod. Podoba mi się ta metoda znacznie lepiej niż pierwsza, ale problem z nią nie wykrywa, czy szablon wywołuje do_shortcode().

Więc skłaniam się ku użyciu drugiej metody, a następnie próbuję wykryć, czy szablon jest przypisany, a jeśli tak, przeanalizuj go pod kątem krótkiego kodu. Jednak zanim to zrobiłem, chciałem sprawdzić, czy ktoś wie o lepszej metodzie.

Aktualizacja: Zintegrowałem rozwiązanie z moją wtyczką. Jeśli ktoś jest ciekawy, czy jest on rozwinięty w środowisku na żywo, możesz go pobrać lub przejrzeć .

Aktualizacja 2: Od wersji WordPress 3.3 można teraz wywoływać wp_enqueue_script()bezpośrednio w wywołaniu zwrotnym shortcode , a plik JavaScript będzie wywoływany w stopce dokumentu. Jest to technicznie możliwe również w przypadku plików CSS, ale należy to uznać za złą praktykę, ponieważ wysyłanie CSS poza <head>znacznik narusza specyfikacje W3C, może powodować FOUC i może zmusić przeglądarkę do ponownego renderowania strony.

Ian Dunn
źródło
W przeszłości korzystałem z odmiany metody 1. Załaduj JS dla shortcode w stopce, załaduj CSS dla shortcode in-line. Działa, ale jest hackish. Czekamy na lepsze rozwiązanie.
EAMann
Główne problemy z wbudowanym CSS polegają na tym, że trudno jest zastąpić inne style i nie używa on metody wp_enqueue_styles ().
Ian Dunn
Ile masz css?
pola
Ile linii javascript?
pola
2
JavaScript jest łatwą częścią. Najlepiej to zrobić pod „Jedi” tutaj: scribu.net/wordpress/optimal-script-loading.html
mfields

Odpowiedzi:

11

Bazując na własnym doświadczeniu, zastosowałem kombinację metody 1 i 2 - architektury i skryptów stopki 1 oraz techniki „przewidywania” 2.

Jednak dla przewidywania używam wyrażenia regularnego zamiast stripos; osobiste preferencje, szybciej i mogą sprawdzić „źle zniekształcony” krótki kod;

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

Jeśli obawiasz się, że autorzy używają do_shortcoderęcznie, zdecyduję się poinstruować ich, aby używali wezwania do działania, aby ręcznie wykasować twój wstępnie zarejestrowany styl.

AKTUALIZACJA : Dla leniwego autora, który nigdy nie RTFM, wyślij komunikat, aby podkreślić błąd swoich sposobów;)

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
źródło
Trafne spostrzeżenie. Idealnie chciałbym, żeby działało, bez konieczności robienia czegokolwiek dodatkowego - ponieważ przez połowę czasu prawdopodobnie nie przeczytają najpierw FAQ, więc po prostu założą, że jest zepsuty - ale mogę to zrobić. Mogłem zarejestrować skrypty na każdej stronie, ale kolejkować je tylko w przypadku wykrycia krótkiego kodu. Następnie użytkownicy mogą w razie potrzeby podłączyć się do init i wywołać funkcje kolejkowania w określonych szablonach, zakładając, że w tym momencie nie jest już za późno na wykonanie. Ponadto WP ma wbudowaną funkcję get_shortcode_regex ().
Ian Dunn
3
Jeśli użytkownicy są biegli we wdrażaniu do_shortcode(), czy nie jest rozsądne zakładać, że są oni biegli w przestrzeganiu instrukcji kolejkowania stylu krótkiego kodu?
Chip Bennett
1
To prawda, ale otrzyma regex dla wszystkich skrótów, nie tylko twoich;) „Mógłbym zarejestrować skrypty na każdej stronie” Prawdopodobnie też lepsza metoda! Pamiętaj, że nie muszą się podłączać init, tylko gdziekolwiek wcześniej wp_head. Dla leniwego programisty sprawdź wp_style_is( 'my_style_handle', 'done' )kod wewnętrzny. Jeśli jest to fałsz, wydrukuj widoczny błąd, który instruuje ich, co robić.
TheDeadMedic
@Chip - Nie martwię się, że nie są w stanie postępować zgodnie z instrukcjami, tylko że nie będą wiedzieć, że powinni, ponieważ 99% czasu nie trzeba robić nic więcej.
Ian Dunn
1
@Ian pomyślałem, że dodanie do_shortcode()do szablonu już „robi [coś] dodatkowego” - a użytkownicy, którzy zrobiliby to coś więcej, albo już wiedzieliby o potrzebie kolejkowania stylu, albo byliby bardziej chętni / prawdopodobnie postępować zgodnie ze specjalnymi instrukcjami.
Chip Bennett
8

Spóźniam się z odpowiedzią na to pytanie, ale odkąd Ian rozpoczął ten wątek na liście wp-hackerów, pomyślałem, że warto odpowiedzieć, szczególnie biorąc pod uwagę, że planuję dodać taką funkcję do niektórych wtyczek, nad którymi pracuję.

Podejście, które należy rozważyć, to sprawdzenie przy ładowaniu pierwszej strony, aby sprawdzić, czy shortcode jest rzeczywiście używany, a następnie zapisanie stanu użycia shortcode w meta-kluczu post. Oto jak:

Poradnik krok po kroku

  1. Ustaw $shortcode_usedflagę na 'no'.
  2. W samej funkcji shortcode ustaw $shortcode_usedflagę na 'yes'.
  3. Ustaw 'the_content'priorytet przechwytywania, 12który jest po przetworzeniu przez WordPress skrótów i sprawdź post meta pod kątem ''używania klucza "_has_{$shortcode_name}_shortcode". (Zwracana jest wartość, ''gdy meta-klucz posta nie istnieje dla identyfikatora postu).
  4. Użyj 'save_post'haka, aby usunąć meta postu, usuwając trwałą flagę dla tego postu na wypadek, gdyby użytkownik zmienił użycie krótkiego kodu.
  5. Również w tym 'save_post'haku użyj, wp_remote_request()aby wysłać nieblokujący HTTP GET do bezpośredniego linku do posta, aby uruchomić ładowanie pierwszej strony i ustawienie trwałej flagi.
  6. Wreszcie ustawić 'wp_print_styles'i sprawdzić postu meta na wartości 'yes', 'no'lub ''za pomocą klawisza "_has_{$shortcode_name}_shortcode". Jeśli wartość 'no'nie jest podana na zewnątrz. Jeśli wartość wynosi 'yes'lub ''śmiało i podaj zewnętrzny.

I to powinno wystarczyć. Napisałem i przetestowałem przykładową wtyczkę, aby pokazać, jak to wszystko działa.

Przykładowy kod wtyczki

Wtyczka budzi się na [trigger-css]krótkim kodzie, który ustawia <h2>elementy na stronie na biało-czerwone, abyś mógł łatwo zobaczyć, jak działa. Zakłada csspodkatalog zawierający style.cssplik z tym CSS:

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

Poniżej znajduje się kod działającej wtyczki:

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

Przykładowe zrzuty ekranu

Oto seria zrzutów ekranu

Podstawowy edytor postów, bez zawartości

Wyświetlanie postów, brak treści

Podstawowy edytor [trigger-css]postów z krótkim kodem

Wyświetlanie [trigger-css]postów za pomocą krótkiego kodu

Nie jestem pewien, czy to w 100%

Uważam, że powyższe powinno działać prawie we wszystkich przypadkach, ale ponieważ właśnie napisałem ten kod, nie mogę być w 100% pewien. Jeśli możesz znaleźć sytuacje, w których to nie działa, naprawdę chciałbym wiedzieć, żebym mógł naprawić kod w niektórych wtyczkach, do których właśnie to dodałem. Z góry dziękuję.

MikeSchinkel
źródło
Czy więc pięć wtyczek korzystających z twojego podejścia wyzwala pięć zdalnych żądań za każdym razem, gdy zapisywany jest post? Wolałbym użyć wyrażenia regularnego na post_content. A co ze skrótami w widżetach?
fuxia
@toscho Wyzwolenie ładowania postu jest w rzeczywistości opcjonalne; jest tam tylko po to, aby zapewnić, że użytkownik nie będzie musiał widzieć ładowania pierwszej strony z załadowanymi zewnętrznymi elementami. Jest to również nieblokujące wywołanie, więc teoretycznie nie powinieneś tego zauważyć. W naszym kodzie robimy to w klasie bazowej, więc klasa podstawowa może obsłużyć to tylko raz. Możemy zaczepić 'pre_http_request'hak i wyłączyć wiele połączeń z tym samym adresem URL, gdy 'save_post'hak jest aktywny, ale chciałbym poczekać, aż naprawdę zobaczymy taką potrzebę, nie? Jeśli chodzi o widżety, można go poprawić, ale nie jest to przypadek użycia, na który jeszcze patrzyłem.
MikeSchinkel
1
@toscho - Nie możesz też mieć pewności, że ten kod istnieje, ponieważ inny hak może go usunąć. Jedynym sposobem, aby być pewnym, jest to, czy funkcja shortcode rzeczywiście się uruchomi. Zatem podejście wyrażenia regularnego nie jest w 100% niezawodne.
MikeSchinkel,
Wiem. Nie ma kuloodpornego sposobu na wstrzyknięcie CSS dla skrótów (oprócz używania <style>).
fuxia
Pracuję z implementacją opartą na tym rozwiązaniu i chciałem tylko zauważyć, że ta metoda nie będzie kolejkować w celu podglądu postu / strony.
mor7ifer,
5

Googling znalazł dla mnie potencjalną odpowiedź . Mówię „potencjał”, ponieważ wygląda dobrze, powinien działać, ale nie jestem w 100% przekonany, że to najlepszy sposób:

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

Powinno to być w stanie sprawdzić, czy bieżący post używa krótkiego kodu i odpowiednio dodać arkusz stylów do <head>elementu. Ale nie sądzę, że to zadziała dla strony indeksu (tj. Wielu postów w pętli) ... To także z 2-letniego posta na blogu, więc nie jestem nawet pewien, czy to zadziała z WP 3.1.X .

EAMann
źródło
To nie zadziała, jeśli skrót ma argumenty. Jeśli naprawdę chcesz iść tą drogą, która jest powolna, użyj WP get_shortcode_regex()do wyszukiwania.
onetrickpony
Jak powiedziałem, „potencjalna” odpowiedź, której jeszcze nie testowałem ... :-)
EAMann
Jest to w zasadzie to samo co metoda 2, ale nadal nie sprawdza szablonu dla wywołań do_shortcode ().
Ian Dunn
Dlaczego miałby to robić? Jeśli zadzwonisz do_shortcode()ręcznie w szablonie, to już wiesz, że uruchomisz
krótki kod
Użytkownik nie jest tym, który dzwoni na krótki kod. To jest dla wtyczki rozproszonej, a nie prywatnej.
Ian Dunn
2

Za pomocą kombinacji odpowiedzi TheDeadMedic i na get_shortcode_regex () dokumentacji (która faktycznie nie znaleźć moje shortcodes), stworzyłem prostą funkcję używany do enqueue skryptów dla wielu skróconych. Ponieważ wp_enqueue_script () w skrótach dodaje tylko do stopki, może to być pomocne, ponieważ obsługuje zarówno skrypty nagłówka, jak i stopki.


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
źródło
1

W końcu znalazłem również rozwiązanie warunkowego ładowania css, które działa dla mojej wtyczki www.mapsmarker.com i chciałbym się z tobą podzielić. Sprawdza, czy mój krótki kod jest używany w bieżącym pliku szablonu i nagłówku / stopce.php, a jeśli tak, kolejkuje potrzebny arkusz stylów w nagłówku:

  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
źródło
trochę na bok, ale czy to nie zakłada, że ​​ludzie używają header.php i footer.php. Co z metodami zawijania motywów, takimi jak te opisane przez scribu.net/wordpress/theme-wrappers.html ? lub motywy takie jak korzenie, które przechowują części szablonu w innym miejscu?
orionrush,
1

W przypadku mojej wtyczki stwierdziłem, że czasami użytkownicy mają narzędzie do tworzenia motywów, w którym krótki kod jest przechowywany w metadanych post . Oto, czego używam do wykrycia, czy mój krótki kod wtyczki jest obecny w bieżących postach lub metadanych :

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
źródło
0

ponieważ CSS powinien być zadeklarowany w środku <head>

W przypadku plików CSS możesz je załadować w ramach wyjścia shortcode:

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

Ustaw stałą lub coś po tym, na przykład MY_CSS_LOADED(dołącz CSS tylko wtedy, gdy stała nie jest ustawiona).

Obie metody są wolniejsze niż w tym kierunku.

W przypadku plików JS możesz zrobić to samo, jeśli ładowany skrypt jest unikalny i nie ma żadnych zewnętrznych zależności. Jeśli tak nie jest, załaduj go do stopki, ale użyj stałej, aby ustalić, czy należy załadować, czy nie ...

onetrickpony
źródło
3
Ładowanie CSS poza <head>element nie jest poprawnym znacznikiem. To prawda, że ​​sprawdzanie poprawności jest tylko wytyczną, ale jeśli staramy się trzymać się tej wytycznej, ładowanie arkusza stylów na wyjściu krótkiego kodu jest złym pomysłem.
EAMann
Wewnętrzne bloki CSS są poprawnymi znacznikami, nawet w XHTML z tego, co pamiętam. Nie ma powodu, aby nie korzystać z nich, gdy nie ma innej akceptowalnej alternatywy
onetrickpony
1
Według narzędzia walidacji 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". Tak więc style wbudowane ( <element style="..."></element>) są poprawne, ale <style>elementy wbudowane nie.
EAMann
1
ustaw go jako filtrowalny, a dowolna inna wtyczka lub motyw może zrobić z nim, co zechce. Jeśli skonfigurują filtr, aby zwracał pusty ciąg - nic nie zostanie wydrukowane.
pola
1
Nie wymieniłeś żadnych obiektywnych powodów przeciwnych tej praktyce. W każdym razie to nie ma znaczenia; Widzę tylko dwie możliwości: Zawsze CSS Load / skrypty (ich optymalizacji dla wielkości), lub warunkowe style inline
onetrickpony
0

Script Logic to wtyczka WordPress, która daje pełną kontrolę nad wszystkimi plikami JavaScript i CSS. Za pomocą tej wtyczki możesz warunkowo ładować plik CSS i JS tylko na stronach, w razie potrzeby.

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

Tahir Yasin
źródło
To nie jest tak naprawdę istotne w przypadku tego pytania, które dotyczy kontrolowania kolejkowania z poziomu samej wtyczki. Twoje podejście wymagałoby poproszenia użytkownika o zainstalowanie kolejnej wtyczki, a następnie zrozumienia, jak poprawnie ją skonfigurować.
Ian Dunn