Memuat JavaScript / CSS untuk Shortcode

37

Saya merilis sebuah plugin yang membuat kode pendek dan membutuhkan file JavaScript dan file CSS untuk memuat pada halaman apa pun yang berisi kode pendek itu. Saya hanya bisa membuat skrip / gaya memuat di semua halaman, tapi itu bukan praktik terbaik. Saya hanya ingin memuat file pada halaman yang memanggil kode pendek. Saya telah menemukan dua metode untuk melakukan ini, tetapi keduanya memiliki masalah.

Metode 1 menetapkan bendera menjadi true di dalam fungsi penangan kode pendek, dan lalu memeriksa nilai itu di dalam wp_footerpanggilan balik. Jika benar, ini digunakan wp_print_scripts()untuk memuat JavaScript. Masalahnya adalah ini hanya berfungsi untuk JavaScript dan bukan CSS, karena CSS harus dideklarasikan di dalamnya <head>, yang hanya dapat Anda lakukan pada hook awal seperti initatau wp_head.

Metode 2 diaktifkan lebih awal dan "mengintip ke depan" untuk melihat apakah kode pendek ada di konten halaman saat ini. Saya suka metode ini jauh lebih baik daripada yang pertama, tetapi masalah dengan itu tidak akan mendeteksi jika panggilan template do_shortcode().

Jadi, saya cenderung menggunakan metode kedua dan kemudian mencoba mendeteksi apakah templat ditugaskan, dan jika demikian, parsing untuk kode pendek. Sebelum saya melakukan itu, saya ingin memeriksa apakah ada yang tahu metode yang lebih baik.

Pembaruan: Saya telah mengintegrasikan solusi ke dalam plugin saya. Jika ada yang ingin melihatnya diperbesar dalam lingkungan langsung, Anda dapat mengunduhnya atau menjelajahinya .

Pembaruan 2: Pada WordPress 3.3, sekarang mungkin untuk memanggil wp_enqueue_script()langsung di dalam panggilan balik kode pendek , dan file JavaScript akan dipanggil di dalam footer dokumen. Itu secara teknis mungkin untuk file CSS juga, tetapi harus dianggap sebagai praktik buruk karena mengeluarkan CSS di luar <head>tag melanggar spesifikasi W3C, dapat menggunakan FOUC, dan dapat memaksa browser untuk merender ulang halaman.

Ian Dunn
sumber
Saya telah menggunakan variasi Metode 1 di masa lalu. Muat JS untuk shortcode di footer, muat CSS untuk shortcode in-line. Ini bekerja, tetapi bersifat meretas. Menantikan solusi yang lebih baik.
EAMann
Masalah utama dengan CSS sebaris adalah sulit untuk menimpa dengan gaya lain, dan itu tidak menggunakan metode wp_enqueue_styles ().
Ian Dunn
Berapa banyak css yang kamu punya?
mfields
Berapa banyak baris javascript?
mfields
2
Javascript adalah bagian yang mudah. Apa yang terbaik untuk melakukan ini adalah di bawah "Jedi" di sini: scribu.net/wordpress/optimal-script-loading.html
mfields

Jawaban:

11

Berdasarkan pengalaman saya sendiri, saya telah menggunakan kombinasi metode 1 & 2 - skrip arsitektur dan catatan kaki 1, dan teknik 'melihat ke depan' dari 2.

Untuk melihat-depan, saya menggunakan regex sebagai pengganti stripos; preferensi pribadi, lebih cepat, dan dapat memeriksa kode pendek 'salah bentuk';

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

Jika Anda khawatir tentang penulis menggunakan do_shortcodesecara manual, saya akan memilih untuk menginstruksikan mereka untuk menggunakan panggilan tindakan enqueue gaya pra-terdaftar Anda secara manual.

UPDATE : Untuk penulis malas yang tidak pernah RTFM, mengeluarkan pesan untuk menyoroti kesalahan cara mereka;)

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
sumber
Itu poin yang bagus. Idealnya saya ingin bekerja tanpa mereka harus melakukan sesuatu yang ekstra - karena separuh waktu mereka mungkin tidak akan membaca FAQ terlebih dahulu, jadi mereka hanya akan menganggap itu rusak - tetapi saya mungkin akhirnya melakukan itu. Saya bisa mendaftarkan skrip pada setiap halaman, tetapi hanya meminta mereka jika saya mendeteksi kode pendek. Kemudian, pengguna dapat menghubungkan ke init dan memanggil fungsi enqueue dalam templat tertentu jika diperlukan, dengan asumsi itu belum terlambat dalam eksekusi pada saat itu. Juga, WP memiliki get_shortcode_regex () bawaan.
Ian Dunn
3
Jika pengguna mahir dalam mengimplementasikan do_shortcode(), apakah tidak masuk akal untuk menganggap bahwa mereka juga mahir dalam mengikuti instruksi untuk menentukan gaya kode pendek?
Chip Bennett
1
Benar, tetapi itu akan mendapatkan regex untuk semua kode pendek, bukan hanya milik Anda;) "Saya bisa mendaftarkan skrip pada setiap halaman" Mungkin metode yang lebih baik juga! Perhatikan bahwa mereka tidak perlu terhubung init, hanya di mana saja sebelumnya wp_head. Untuk pengembang yang malas, periksa wp_style_is( 'my_style_handle', 'done' )di dalam kode pendek Anda. Jika itu salah, cetak kesalahan yang terlihat yang memerintahkan mereka apa yang harus dilakukan.
TheDeadMedic
@Chip - Saya tidak khawatir mereka tidak mampu mengikuti instruksi, hanya saja mereka tidak akan tahu mereka seharusnya, karena 99% dari waktu Anda tidak perlu melakukan sesuatu yang ekstra.
Ian Dunn
1
@Ada pemikiran saya adalah bahwa menambahkan do_shortcode()ke templat sudah "melakukan sesuatu yang ekstra" - dan pengguna yang akan melakukan sesuatu itu ekstra akan sudah tahu tentang perlunya enqueue gaya, atau yang lain akan lebih bersedia / mungkin untuk mengikuti instruksi khusus.
Chip Bennett
8

Saya terlambat menjawab pertanyaan ini, tetapi karena Ian memulai utas ini pada daftar wp-hacker hari ini membuat saya berpikir itu layak untuk dijawab terutama mengingat saya telah berencana untuk menambahkan fitur seperti itu ke beberapa plugin yang telah saya kerjakan.

Pendekatan yang perlu dipertimbangkan adalah memeriksa pada pemuatan halaman pertama untuk melihat apakah kode pendek benar-benar digunakan dan kemudian menyimpan status penggunaan kode pendek ke kunci meta pos. Begini caranya:

Langkah-demi-Langkah

  1. Tetapkan $shortcode_usedbendera ke 'no'.
  2. Dalam fungsi shortcode itu sendiri mengatur $shortcode_usedbendera ke 'yes'.
  3. Tetapkan 'the_content'prioritas kait 12yang setelah WordPress memproses kode pendek dan periksa meta pos untuk ''menggunakan kunci "_has_{$shortcode_name}_shortcode". (Nilai ''dikembalikan ketika kunci meta pos tidak ada untuk ID pos.)
  4. Gunakan 'save_post'pengait untuk menghapus meta pos yang menghapus bendera persisten untuk kiriman tersebut jika pengguna mengubah penggunaan kode pendek.
  5. Juga dalam penggunaan 'save_post'hook wp_remote_request()untuk mengirim GET HTTP non-blocking ke permalink posting sendiri untuk memicu memuat halaman pertama dan pengaturan flag persistent.
  6. Terakhir, tetapkan 'wp_print_styles'dan periksa meta pos untuk nilai 'yes', 'no'atau ''gunakan kunci "_has_{$shortcode_name}_shortcode". Jika nilainya 'no'tidak melayani eksternal. Jika nilainya 'yes'atau ''maju dan melayani eksternal.

Dan itu harus dilakukan. Saya telah menulis dan menguji contoh plugin untuk menunjukkan bagaimana semua ini bekerja.

Contoh Kode Pengaya

Plugin bangun dengan [trigger-css]kode pendek yang mengatur <h2>elemen - elemen pada halaman menjadi putih-merah sehingga Anda dapat melihatnya bekerja dengan mudah. Ini mengasumsikan csssubdirektori yang berisi style.cssfile dengan CSS ini di dalamnya:

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

Dan di bawah ini adalah kode di plugin yang berfungsi:

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

Contoh Screenshot

Berikut serangkaian tangkapan layar

Editor Posting Dasar, Tidak Ada Konten

Tampilan Posting, Tidak Ada Konten

Editor Posting Dasar dengan [trigger-css]Kode Pendek

Posting Tampilan dengan [trigger-css]Shortcode

Tidak yakin jika 100%

Saya percaya di atas harus bekerja di hampir semua kasus tetapi karena saya baru saja menulis kode ini saya tidak bisa 100% yakin. Jika Anda dapat menemukan situasi di mana itu tidak berhasil saya benar-benar ingin tahu sehingga saya dapat memperbaiki kode di beberapa plugin saya baru saja menambahkan ini. Terima kasih sebelumnya.

MikeSchinkel
sumber
Jadi lima plugin menggunakan pendekatan Anda akan memicu lima permintaan jarak jauh setiap kali posting disimpan? Saya lebih suka menggunakan regex post_content. Dan bagaimana dengan shortcode dalam widget?
fuxia
@toscho Memicu pemuatan pos sebenarnya opsional; itu hanya ada untuk memastikan pengguna tidak harus melihat halaman pertama dimuat dengan eksternal dimuat. Ini juga merupakan panggilan yang tidak menghalangi sehingga secara teori Anda seharusnya tidak menyadarinya. Untuk kode kami, kami melakukannya di kelas dasar sehingga kelas dasar dapat menangani melakukannya hanya sekali. Kita bisa mengaitkan 'pre_http_request'kail dan menonaktifkan beberapa panggilan ke URL yang sama saat 'save_post'kail aktif, tetapi saya ingin menunggu sampai kita benar-benar melihat kebutuhan untuk itu, bukan? Adapun Widgets bisa ditingkatkan untuk menangani tapi itu bukan kasus penggunaan yang saya lihat.
MikeSchinkel
1
@toscho - Anda juga tidak dapat memastikan bahwa ada kode pendek karena pengait lain dapat menghapusnya. Satu-satunya cara Anda bisa memastikan adalah apakah fungsi kode pendek benar-benar menyala. Jadi pendekatan regex tidak 100% andal.
MikeSchinkel
Aku tahu. Tidak ada cara anti peluru untuk menyuntikkan CSS untuk kode pendek (kecuali menggunakan <style>).
fuxia
Saya sedang bekerja dengan implementasi berdasarkan solusi ini dan saya hanya ingin menunjukkan bahwa metode ini tidak akan memerlukan pratinjau posting / halaman.
mor7ifer
5

Googling menemukan saya jawaban potensial . Saya mengatakan "potensi" karena terlihat bagus, harus bekerja, tetapi saya tidak 100% yakin itu cara terbaik untuk melakukannya:

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

Ini harus dapat memeriksa apakah posting saat ini menggunakan kode pendek dan menambahkan stylesheet ke <head>elemen dengan tepat. Tapi saya tidak berpikir itu akan berfungsi untuk halaman indeks (yaitu beberapa posting di loop) ... Ini juga dari posting blog lama 2-tahun, jadi saya bahkan tidak yakin itu akan bekerja dengan WP 3.1.X .

EAMann
sumber
Ini tidak akan berfungsi jika kode pendek memiliki argumen. Jika Anda benar-benar ingin pergi dengan cara ini, yang lambat, gunakan WP get_shortcode_regex()untuk mencari.
onetrickpony
Seperti yang saya katakan, jawaban "potensial" yang belum saya uji ... :-)
EAMann
Itu pada dasarnya sama dengan metode 2, tetapi masih tidak memeriksa template untuk panggilan do_shortcode ().
Ian Dunn
Mengapa perlu melakukan itu? Jika Anda menelepon do_shortcode()secara manual dalam templat maka Anda sudah tahu Anda akan menjalankan kode pendek
onetrickpony
Saya bukan orang yang memanggil kode pendek, pengguna. Ini untuk plugin yang didistribusikan, bukan plugin pribadi.
Ian Dunn
2

Dengan menggunakan kombinasi jawaban TheDeadMedic dan dokumentasi get_shortcode_regex () (yang sebenarnya tidak menemukan kode pendek saya), saya membuat fungsi sederhana yang digunakan untuk membuat skrip enqueue untuk beberapa kode pendek. Karena wp_enqueue_script () dalam shortcode hanya menambah footer, ini dapat membantu karena dapat menangani skrip header dan footer.


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
sumber
1

Akhirnya saya juga menemukan solusi untuk pemuatan css bersyarat yang berfungsi untuk plugin saya www.mapsmarker.com dan saya ingin berbagi dengan Anda. Ia memeriksa apakah kode pendek saya digunakan dalam file templat saat ini dan header / footer.php dan jika ya, enqueues stylesheet yang diperlukan di header:

  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
sumber
sedikit dari samping tetapi ini tidak menganggap bahwa orang menggunakan header.php dan footer.php. Bagaimana dengan metode pembungkus tema seperti yang dijelaskan oleh scribu.net/wordpress/theme-wrappers.html ? atau tema seperti root yang menyimpan bagian templat mereka di tempat lain?
orionrush
1

Untuk plugin saya, saya menemukan bahwa kadang-kadang pengguna memiliki pembuat tema yang memiliki kode pendek yang disimpan dalam data meta pos . Inilah yang saya gunakan untuk mendeteksi apakah kode pendek plugin saya ada di pos saat ini atau data meta pos :

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
sumber
0

karena CSS harus dideklarasikan di dalam <head>

Untuk file CSS Anda dapat memuatnya dalam output kode pendek Anda:

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

Tetapkan konstanta atau sesuatu setelah ini, seperti MY_CSS_LOADED(hanya sertakan CSS jika konstanta tidak disetel).

Kedua metode Anda lebih lambat daripada berjalan dengan cara ini.

Untuk file JS Anda dapat melakukan hal yang sama jika skrip yang Anda muat unik dan tidak memiliki dependensi luar. Jika ini bukan case memuatnya di dalam footer, tetapi gunakan konstanta untuk menentukan apakah perlu dimuat atau tidak ...

onetrickpony
sumber
3
Memuat CSS di luar <head>elemen bukan markup yang tepat. Benar, validasi hanyalah sebuah panduan, tetapi jika kita mencoba untuk tetap berpegang pada pedoman itu, membuat memuat stylesheet dalam output shortcode adalah ide yang buruk.
EAMann
Blok CSS sebaris adalah markup yang valid, bahkan dalam XHTML dari apa yang saya ingat. Tidak ada alasan untuk tidak menggunakannya ketika Anda tidak memiliki alternatif lain yang dapat diterima
onetrickpony
1
Menurut alat validasi 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". Jadi gaya inline ( <element style="..."></element>) valid, tetapi <style>elemen inline tidak.
EAMann
1
membuatnya dapat difilter dan plugin atau tema lain apa pun dapat melakukannya sesuka mereka. Jika mereka mengonfigurasi filter untuk mengembalikan string kosong - tidak ada yang akan dicetak.
mfields
1
Anda tidak menyebutkan alasan obyektif terhadap praktik ini. Bagaimanapun itu tidak masalah; Saya hanya melihat dua opsi di sini: Selalu memuat CSS / skrip (optimalkan untuk ukuran), atau gaya inline bersyarat
onetrickpony
0

Script Logic adalah plugin WordPress yang memberi Anda kendali penuh atas semua file JavaScript dan CSS. Dengan menggunakan plugin ini, Anda dapat memuat file CSS dan JS secara kondisional hanya pada halaman yang diperlukan.

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

Tahir Yasin
sumber
Itu tidak benar-benar relevan dengan pertanyaan ini, yaitu tentang mengontrol enqueueing dari dalam plugin itu sendiri. Pendekatan Anda akan membutuhkan meminta pengguna untuk menginstal plugin lain, dan kemudian memahami cara mengkonfigurasi dengan benar.
Ian Dunn