Înregistrare Scripts / Styles când este prezent un shortcode
Care este cea mai bună modalitate de a înregistra/încărca scripturi și/sau stiluri pentru utilizarea în plugin-uri?
Recent am creat un plugin simplu pentru a adăuga avatarul/gravatarul utilizatorului cu un shortcode. Am diferite opțiuni de stil pentru afișarea avatarului (pătrat, rotund etc.) și am decis să pun CSS-ul direct în shortcode.
Totuși, îmi dau seama acum că această abordare nu este bună deoarece va repeta CSS-ul de fiecare dată când shortcode-ul este utilizat într-o pagină. Am văzut mai multe abordări pe acest site și chiar codexul WP are două exemple proprii, așa că este greu de știut ce abordare este mai consistentă și mai rapidă.
Iată metodele de care sunt conștient în prezent:
Metoda 1: Include direct în shortcode - Aceasta este ce fac momentan în plugin, dar nu pare bine deoarece repetă codul.
class My_Shortcode {
function handle_shortcode( $atts, $content="" ) {
/* pur și simplu încarcă sau afișează scripturile/stilurile în shortcode-ul în sine */
?>
<style type="text/css">
</style>
<?php
return "$content";
}
}
add_shortcode( 'myshortcode', array( 'My_Shortcode', 'handle_shortcode' ) );
Metoda 2: Folosește clasa pentru încărcarea condițională a scripturilor sau stilurilor
class My_Shortcode {
static $add_script;
static function init() {
add_shortcode('myshortcode', array(__CLASS__, 'handle_shortcode'));
add_action('init', array(__CLASS__, 'register_script'));
add_action('wp_footer', array(__CLASS__, 'print_script'));
}
static function handle_shortcode($atts) {
self::$add_script = true;
// manipularea shortcode-ului aici
}
static function register_script() {
wp_register_script('my-script', plugins_url('my-script.js', __FILE__), array('jquery'), '1.0', true);
}
static function print_script() {
if ( ! self::$add_script )
return;
wp_print_scripts('my-script');
}
}
My_Shortcode::init();
Metoda 3: Folosind get_shortcode_regex();
function your_prefix_detect_shortcode() {
global $wp_query;
$posts = $wp_query->posts;
$pattern = get_shortcode_regex();
foreach ($posts as $post){
if ( preg_match_all( '/'. $pattern .'/s', $post->post_content, $matches )
&& array_key_exists( 2, $matches )
&& in_array( 'myshortcode', $matches[2] ) )
{
// css/js
break;
}
}
}
add_action( 'wp', 'your_prefix_detect_shortcode' );
Metoda 4: Folosind has_shortcode();
function custom_shortcode_scripts() {
global $post;
if( is_a( $post, 'WP_Post' ) && has_shortcode( $post->post_content, 'myshortcode') ) {
wp_enqueue_script( 'my-script');
}
}
add_action( 'wp_enqueue_scripts', 'custom_shortcode_scripts');

Am găsit o altă metodă care funcționează bine pentru mine:
La inițializarea plugin-ului, nu încărcați scripturile și stilurile, ci înregistrați-le cu
wp_register_style
șiwp_register_script
.Apoi puteți încărca scriptul/stilul la cerere. De exemplu, când redați un shortcode cu
wp_enqueue_style("your_style")
șiwp_enqueue_script("your_script")
.
Iată un exemplu de plugin care utilizează această metodă, permițându-vă să folosiți get_avatar într-un shortcode. Fișierul de stil este încărcat doar atunci când shortcode-ul este prezent.
Utilizare (id implică utilizatorul curent):
[get_avatar id="" size="32" default="mystery" alt="Poza de profil" class="round"]
function wpse_165754_avatar_shortcode_wp_enqueue_scripts() {
wp_register_style( 'get-avatar-style', plugins_url( '/css/style.css', __FILE__ ), array(), '1.0.0', 'all' );
}
add_action( 'wp_enqueue_scripts', 'wpse_165754_avatar_shortcode_wp_enqueue_scripts' );
if ( function_exists( 'get_avatar' ) ) {
function wpse_165754_user_avatar_shortcode( $attributes ) {
global $current_user;
get_currentuserinfo();
extract( shortcode_atts(
array(
"id" => $current_user->ID,
"size" => 32,
"default" => 'mystery',
"alt" => '',
"class" => '',
), $attributes, 'get_avatar' ) );
$get_avatar = get_avatar( $id, $size, $default, $alt );
wp_enqueue_style( 'get-avatar-style' );
return '<span class="get_avatar ' . $class . '">' . $get_avatar . '</span>';
}
add_shortcode( 'get_avatar', wpse_165754_user_avatar_shortcode' );
}

Am uitat că am pus această întrebare anul trecut, dar de fapt am început să fac și eu în felul acesta în multe cazuri. E un fel de combinație între metoda 1 și 2.

Această metodă încarcă CSS-ul în footer, ceea ce cu siguranță are limitări?

Aceasta este cu siguranță metoda corectă de a face acest lucru. Folosește această metodă ori de câte ori ai nevoie să încarci condiționat un script sau o foaie de stil după ce hook-ul wp_enqueue_scripts
a avut deja loc. (Shortcode-urile sunt parsate în timpul the_content
, care face parte din hook-ul the_post
, ceea ce înseamnă că încărcarea lor în momentul parsării este prea târziu, cu excepția cazului în care scriptul a fost deja înregistrat)

Înainte de a începe răspunsul, trebuie să menționez că în ceea ce privește acest subiect, CSS și JS nu sunt același lucru.
Motivul este simplu: în timp ce adăugarea JS în corpul paginii (în footer) este o metodă comună și validă, CSS trebuie plasat în secțiunea <head>
a paginii: chiar dacă majoritatea browserelor pot afișa corect CSS în corpul paginii, acest lucru nu este un cod HTML valid.
Când un shortcode este randat, secțiunea <head>
a fost deja afișată, ceea ce înseamnă că JS poate fi adăugat fără probleme în footer, dar CSS trebuie adăugat înainte ca shortcode-ul să fie randat.
Scripturi
Dacă shortcode-ul tău are nevoie doar de JS, ai noroc și poți folosi direct wp_enqueue_script
în corpul funcției de callback a shortcode-ului:
add_shortcode( 'myshortcode', 'my_handle_shortcode' );
function my_handle_shortcode() {
wp_enqueue_script( 'myshortcodejs', '/calea/către/fisier.js' );
// restul codului aici...
}
Astfel, scriptul tău va fi adăugat în footer și doar o singură dată, chiar dacă shortcode-ul este folosit de mai multe ori în pagină.
Stiluri
Dacă codul tău necesită stiluri, atunci trebuie să acționezi înainte ca shortcode-ul să fie randat.
Există diferite metode pentru a face acest lucru:
verifică toate articolele din interogarea curentă și adaugă stilurile shortcode-ului dacă este necesar. Aceasta este metoda pe care o folosești atât în metoda #3 cât și în #4 din OP. De fapt, ambele metode fac același lucru, dar
has_shortcode
a fost adăugat în WP 3.6, în timp ceget_shortcode_regex
este disponibil începând cu versiunea 2.5, așa că foloseșteget_shortcode_regex
doar dacă vrei să faci plugin-ul compatibil cu versiuni mai vechi.adaugă mereu stilurile shortcode-ului, în toate paginile
Probleme
Problema cu #1 este performanța. Expresiile regulate (regex) sunt operații destul de lente, iar rularea lor într-o buclă pentru toate articolele poate încetini semnificativ pagina. În plus, este o practică comună în teme să afișezi doar fragmentul articolului în arhive și conținutul complet cu shortcode-uri doar în vizualizările individuale. Dacă acest lucru se întâmplă, când este afișată o arhivă, plugin-ul tău va rula o potrivire regex într-o buclă cu scopul de a adăuga un stil care nu va fi niciodată folosit: un impact dublu și inutil asupra performanței: încetinirea generării paginii + cerere HTTP suplimentară inutilă.
Problema cu #2 este, din nou, performanța. Adăugarea stilului în toate paginile înseamnă o cerere HTTP suplimentară pentru toate paginile, chiar și atunci când nu este necesar. Chiar dacă timpul de generare al paginii pe server nu este afectat, timpul total de randare al paginii va fi afectat, și asta pentru toate paginile site-ului.
Deci, ce ar trebui să facă un dezvoltator de plugin-uri?
Cred că cel mai bine este să adaugi o pagină de opțiuni în plugin, unde utilizatorii pot alege dacă shortcode-ul ar trebui gestionat doar în vizualizări individuale sau și în arhive. În ambele cazuri, este mai bine să oferi o altă opțiune pentru a alege pentru care tipuri de postări să activezi shortcode-ul.
În acest fel, este posibil să folosești hook-ul "template_redirect"
pentru a verifica dacă interogarea curentă îndeplinește cerințele și, în acest caz, să adaugi stilul.
Dacă utilizatorul alege să folosească shortcode-ul doar în vizualizări individuale de postări, este o idee bună să verifici dacă postarea conține shortcode-ul sau nu: deoarece este necesară doar o singură expresie regulată, aceasta nu ar trebui să încetinească prea mult pagina.
Dacă utilizatorul alege să folosească shortcode-ul și în arhive, atunci aș evita să rulez regex pentru toate postările dacă numărul acestora este mare și aș încărca stilul doar dacă interogarea îndeplinește cerințele.
Ceea ce considerăm "mare" în acest context ar trebui stabilit prin teste de performanță sau, ca alternativă, să adaugi o altă opțiune și să lași alegerea utilizatorilor.

Este important de menționat că etichetele <style>
în corpul documentului sunt acum valide conform specificației W3C. https://www.w3.org/TR/html52/document-metadata.html#the-style-element

@IsaacLubow În prezent (2023): Contexturi în care acest element poate fi utilizat: Într-un element noscript care este copil al unui element head. Am validat <style>
în interiorul <body>
pe https://validator.w3.org/nu/#textarea dar rezultatul este Eroare: Elementul style
nu este permis ca descendent al elementului body
în acest context.

Pentru plugin-ul meu am descoperit că uneori utilizatorii au un constructor de teme care stochează shortcode-uri în metadatele postării. Iată ce folosesc pentru a detecta dacă shortcode-ul meu este prezent în postarea curentă sau în metadatele postării:
function abcd_load_my_shorcode_resources() {
global $post, $wpdb;
// determină dacă această pagină conține shortcode-ul "my_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' );

WordPress are o funcție încorporată pentru a face ceva în funcție de starea de prezentare a unui anumit Shortcode. Numele funcției este has_shortcode()
. Puteți utiliza următorul cod pentru a încărca stilurile și scripturile dumneavoastră.
Aici am folosit is_a( $post, 'WP_Post' )
pentru a verifica dacă obiectul $post
este din clasa WP_Post
și am folosit $post->post_content
pentru a verifica conținutul postării.
if ( is_a( $post, 'WP_Post' ) && has_shortcode( $post->post_content, 'shortcode_tag') ) {
wp_enqueue_style( 'handle', get_template_directory_uri() . '/your_file_filename.css' );
}

Eu fac asta:
class My_Shortcode {
function __construct() {
do_action('my_start_shortcode'); // apelează
....
și prind hook-ul în alte funcții (sau alte plugin-uri):
function wpse_3232_load_script(){
wp_enqueue_script('js-myjs');
}
add_action('my_start_shortcode','wpse_3232_load_script',10);

După cum a menționat cineva, utilizarea funcției 'has_shortcode' va rezolva problema. Iată exemplul pe care l-am găsit foarte util (Sursa: Wordpress.org)
function wpdocs_shortcode_scripts() {
global $post;
if ( is_a( $post, 'WP_Post' ) && has_shortcode( $post->post_content, 'wpdocs-shortcode') ) {
wp_enqueue_script( 'wpdocs-script');
}
}
add_action( 'wp_enqueue_scripts', 'wpdocs_shortcode_scripts');

Putem încărca condiționat fișiere CSS și JS prin intermediul shortcode-urilor fără a folosi funcții prea elaborate:
Mai întâi, să presupunem că am înregistrat toate fișierele noastre CSS/JS anterior (poate când acțiunea wp_enqueue_scripts
a rulat)
function prep_css_and_js() {
wp_register_style('my-css', $_css_url);
wp_register_script('my-js', $_js_url);
}
add_action( 'wp_enqueue_scripts', 'prep_css_and_js', 5 );
În continuare, să examinăm funcția noastră de shortcode:
function my_shortcode_func( $atts, $content = null ) {
if( ! wp_style_is( "my-css", $list = 'enqueued' ) ) { wp_enqueue_style('my-css'); }
if( ! wp_script_is( "my-js", $list = 'enqueued' ) ) { wp_enqueue_script('my-js'); }
...
}
Simplu. Gata. Prin urmare: putem "încărca condiționat" fișiere css/js, (doar când shortcode-ul [shortcode] rulează).
Să luăm în considerare următoarea ideologie:
- Vrem să încărcăm anumite fișiere CSS/JS (dar doar când shortcode-ul este declanșat)
- Poate nu vrem ca aceste fișiere să se încarce pe fiecare pagină sau deloc dacă shortcode-ul nu este folosit pe o pagină/post. (ușor)
- Putem realiza acest lucru asigurându-ne că WP înregistrează TOATE fișierele css/js înainte de apelarea shortcode-ului și înainte de a fi adăugate în coadă.
- Adică: Poate în funcția mea
prep_css_and_js()
încarc TOATE fișierele .css/.js care se află în anumite foldere...
Acum că WP le vede ca scripturi înregistrate, le putem apela, condiționat, pe baza unei varietăți de situații, inclusiv dar nu limitat la my_shortcode_func()
Beneficii: (fără MySQL, fără funcții avansate și fără filtre necesare)
- acest lucru este "ortodox WP", elegant, rapid, ușor și simplu
- permite compilatoarelor/minificatoarelor să detecteze astfel de scripturi
- folosește funcțiile WP (wp_register_style/wp_register_script)
- compatibil cu mai multe teme/plugin-uri care ar putea dori să dezînregistreze acele scripturi din diverse motive.
- nu se va declanșa de mai multe ori
- foarte puțină procesare sau cod este implicat
Referințe:

Am descoperit pentru propriul meu plugin, cum să verific dacă shortcode-ul este prezent în widget-ul text.
function check_shortcode($text) {
$pattern = get_shortcode_regex();
if ( preg_match_all( '/'. $pattern .'/s', $text, $matches )
&& array_key_exists( 2, $matches )
&& in_array( 'myshortcode', $matches[2] ) )
{
// shortcode-ul meu este folosit
// încarcă fișierele css și js
/****** Încărcare RFNB ******/
wp_enqueue_style('css-mycss');
wp_enqueue_script('js-myjs');
// OPȚIONAL PERMITE FUNCȚIONAREA SHORTCODE ÎN WIDGET-UL TEXT
add_filter( 'widget_text', 'shortcode_unautop');
add_filter( 'widget_text', 'do_shortcode');
}
return $text;
}
add_filter('widget_text', 'check_shortcode');

Am făcut o combinație între codul exemplu de pe pagina WordPress pentru has_shortcode() și răspunsul dat de zndencka. Am observat că funcția has_shortcode a fost adăugată în WordPress 3.6, de aceea verific mai întâi dacă funcția există. Poate că această verificare este puțin învechită, deoarece nu mai sunt mulți utilizatori cu versiuni sub WordPress 3.6, conform statisticilor oficiale WordPress.
// Acest cod asigură că stilizarea este deja încărcată în header, înainte ca shortcode-ul să se încarce în pagină/post
function enqueue_shortcode_header_script() {
global $post;
if ( function_exists( 'has_shortcode' ) ){ // a fost adăugată în WordPress 3.6
// Sursă: https://codex.wordpress.org/Function_Reference/has_shortcode
if( is_a( $post, 'WP_Post' ) && has_shortcode( $post->post_content, 'my_shortcode') ) {
wp_enqueue_style( 'shortcode-css' );
}
}
else if ( isset($post->ID) ) { // Un procent mic de utilizatori WordPress sunt sub 3.6 https://wordpress.org/about/stats/
global $wpdb;
$result = $wpdb->get_var(
$wpdb->prepare(
"SELECT count(*) FROM $wpdb->postmeta " .
"WHERE post_id = %d and meta_value LIKE '%%my_shortcode%%'",
$post->ID )
);
if (!empty( $result )) { wp_enqueue_style( 'shortcode-css' ); }
}
}
add_action( 'wp_enqueue_scripts', 'enqueue_shortcode_header_script');

Eu folosesc WordPress Versiunea 5.4 cu stilul OOP de cod și nu știu dacă acest lucru afectează motivul pentru care niciuna din soluțiile de mai sus nu a funcționat pentru mine, așa că am venit cu această soluție:
public function enqueue_scripts() {
global $post;
//error_log( print_r( $post, true ) );
//error_log( print_r( $post->post_content, true ) );
//error_log( print_r( strpos($post->post_content, '[YOUR_SHORTCODE]'),true));
if ( is_a( $post, 'WP_Post' ) && strpos($post->post_content, '[YOUR_SHORTCODE]') )
{
wp_register_style('my-css', $_css_url);
wp_register_script('my-js', $_js_url);
}
}
Sper că acest lucru ajută pe cineva.
