Carregamento condicional de JavaScript / CSS para códigos de acesso

37

Lancei um plug-in que cria um código curto e requer um arquivo JavaScript e um arquivo CSS para carregar em qualquer página que contenha esse código curto. Eu poderia carregar o script / estilo em todas as páginas, mas essa não é a melhor prática. Eu só quero carregar os arquivos nas páginas que chamam o shortcode. Eu encontrei dois métodos para fazer isso, mas ambos têm problemas.

O método 1 define um sinalizador como true dentro da função de manipulador de códigos curtos e verifica esse valor dentro de um wp_footerretorno de chamada. Se for verdade, ele usa wp_print_scripts()para carregar o JavaScript. O problema disso é que ele só funciona para JavaScript e não para CSS, porque o CSS deve ser declarado dentro <head>, o que você só pode fazer durante um gancho inicial como initou wp_head.

O método 2 é acionado mais cedo e "espreita à frente" para verificar se o código de acesso existe no conteúdo da página atual. Eu gosto desse método muito melhor do que o primeiro, mas o problema com ele não detectará se o modelo chama do_shortcode().

Portanto, estou inclinado a usar o segundo método e, em seguida, tentando detectar se um modelo está atribuído e, em caso afirmativo, analise-o para obter o código de acesso. Antes de fazer isso, porém, eu queria verificar se alguém conhece um método melhor.

Atualização: integrei a solução ao meu plugin. Se alguém estiver curioso para vê-lo aprimorado em um ambiente ao vivo, faça o download ou navegue nele .

Atualização 2: A partir do WordPress 3.3, agora é possível chamar wp_enqueue_script()diretamente dentro de um retorno de chamada de código curto , e o arquivo JavaScript será chamado no rodapé do documento. Isso também é tecnicamente possível para arquivos CSS, mas deve ser considerado uma prática ruim, pois a saída de CSS fora da <head>tag viola as especificações do W3C, pode causar FOUC e forçar o navegador a renderizar novamente a página.

Ian Dunn
fonte
Eu usei uma variação do método 1 no passado. Carregue JS para o shortcode no rodapé, carregue CSS para o shortcode em linha. Funciona, mas é imprudente. Ansioso por uma solução melhor.
EAMann
Os principais problemas com CSS embutido são que é difícil substituir outros estilos e não usa o método wp_enqueue_styles ().
Ian Dunn
Quanto css você tem?
mfields
Quantas linhas de javascript?
mfields
2
O javascript é a parte mais fácil. Melhor wat para fazer isso é em "Jedi" aqui: scribu.net/wordpress/optimal-script-loading.html
mfields

Respostas:

11

Com base em minha própria experiência, usei uma combinação do método 1 e 2 - a arquitetura e os scripts de rodapé de 1 e a técnica de "antecipação" de 2.

Para o futuro, porém, uso regex no lugar de stripos; preferência pessoal, mais rápida e pode verificar se há um código de acesso 'malformado';

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

Se você estiver preocupado com os autores que usam do_shortcodemanualmente, eu optaria por instruí-los a usar uma chamada de ação enfileirando seu estilo pré-registrado manualmente.

ATUALIZAÇÃO : Para o autor preguiçoso que nunca RTFM, envie uma mensagem para destacar o erro de seus caminhos;)

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
fonte
Este é um bom ponto. Idealmente, eu gostaria que funcionasse sem que eles tivessem que fazer algo extra - porque na metade do tempo eles provavelmente não leem primeiro o FAQ, então eles assumem que está quebrado - mas eu posso acabar fazendo isso. Eu poderia registrar os scripts em todas as páginas, mas somente colocá-los em fila se detectar um código curto. Em seguida, os usuários podem se conectar ao init e chamar as funções de enfileiramento em modelos específicos, sempre que necessário, supondo que ainda não seja tarde demais na execução nesse ponto. Além disso, o WP possui o get_shortcode_regex () embutido.
Ian Dunn
3
Se os usuários são adeptos da implementação do_shortcode(), não é razoável supor que eles também sejam adeptos de seguir as instruções para enfileirar o estilo de código de acesso?
Chip Bennett
11
É verdade, mas ele obterá o regex para todos os códigos de acesso, não apenas os seus;) "Eu poderia registrar os scripts em todas as páginas" Provavelmente, o melhor método também! Observe que eles não precisam se conectar init, em qualquer lugar antes wp_head. Para o desenvolvedor preguiçoso, verifique wp_style_is( 'my_style_handle', 'done' )dentro do seu código curto. Se for falso, imprima um erro visível que os instrui sobre o que fazer.
TheDeadMedic
@Chip - Não estou preocupado que eles não sejam capazes de seguir as instruções, apenas que eles não saberão o que devem, pois 99% do tempo você não precisa fazer nada extra.
Ian Dunn
11
@Posso pensar que a adição do_shortcode()ao modelo está "fazendo algo extra" - e os usuários que fazem isso extra já sabem da necessidade de enfileirar o estilo ou então estão mais dispostos / prováveis para seguir instruções especiais.
Chip Bennett
8

Estou atrasado em responder a essa pergunta, mas desde que Ian começou esse tópico na lista wp-hackers hoje, me fez pensar que vale a pena responder, especialmente considerando que planejava adicionar esse recurso a alguns plugins em que estava trabalhando.

Uma abordagem a considerar é verificar o carregamento da primeira página para ver se o código de acesso é realmente usado e, em seguida, salvar o status de uso do código de acesso em uma meta-chave de postagem. Aqui está como:

Instruções passo a passo

  1. Defina um $shortcode_usedsinalizador para 'no'.
  2. Na própria função shortcode, defina o $shortcode_usedsinalizador para 'yes'.
  3. Defina uma 'the_content'prioridade de gancho 12após o WordPress ter processado os códigos de acesso e verifique a meta de postagem para ''usar a chave "_has_{$shortcode_name}_shortcode". (Um valor de ''é retornado quando uma meta-chave de postagem não existe para o ID da postagem.)
  4. Use um 'save_post'gancho para excluir a meta meta de postagem, limpando o sinalizador persistente dessa postagem, caso o usuário altere o uso de códigos de acesso.
  5. Também no 'save_post'gancho, use wp_remote_request()para enviar um HTTP GET sem bloqueio para o próprio link permanente da postagem para acionar o carregamento da primeira página e a configuração do sinalizador persistente.
  6. Por fim definir um 'wp_print_styles'e verificar meta post para um valor de 'yes', 'no'ou ''utilizando a tecla "_has_{$shortcode_name}_shortcode". Se o valor for 'no', não atenda ao externo. Se o valor for 'yes'ou ''vá em frente e sirva o externo.

E isso deveria bastar. Escrevi e testei um exemplo de plug-in para mostrar como tudo isso funciona.

Exemplo de código de plug-in

O plug-in é ativado em um [trigger-css]código de acesso que define os <h2>elementos da página em branco sobre vermelho, para que você possa vê-lo funcionando com facilidade. Ele assume um csssubdiretório que contém o style.cssarquivo com este CSS:

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

E abaixo está o código em um plug-in de trabalho:

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

Capturas de tela de exemplo

Aqui está uma série de capturas de tela

Editor de postagem básico, sem conteúdo

Publicar exibição, sem conteúdo

Editor de postagem básico com [trigger-css]Shortcode

Pós-exibição com [trigger-css]Shortcode

Não tenho certeza se é 100%

Eu acredito que o acima deve funcionar em quase todos os casos, mas como acabei de escrever este código, não posso ter 100% de certeza. Se você encontrar situações em que isso não funcione, eu realmente gostaria de saber para que eu possa corrigir o código em alguns plugins que acabei de adicionar. Desde já, obrigado.

MikeSchinkel
fonte
Então, cinco plugins usando sua abordagem acionarão cinco solicitações remotas cada vez que uma postagem é salva? Eu prefiro usar uma regex post_content. E quanto aos códigos de acesso nos widgets?
fuxia
@toscho O acionamento do pós-carregamento é realmente opcional; está lá apenas para garantir que um usuário não precise ver a primeira página carregar com os externos carregados. Também é uma ligação sem bloqueio, portanto, em teoria, você não deve perceber. Para o nosso código, estamos fazendo em uma classe base, para que a classe base possa lidar com isso apenas uma vez. Poderíamos ligar o 'pre_http_request'gancho e desativar várias chamadas para o mesmo URL enquanto o 'save_post'gancho estiver ativo, mas eu gostaria de esperar até que realmente viesse a necessidade disso, não? Quanto aos Widgets, ele poderia ser aprimorado, mas ainda não é um caso de uso.
MikeSchinkel
11
@toscho - Além disso, você não pode ter certeza de que o código curto está lá, pois outro gancho pode limpá-lo. A única maneira de ter certeza é se a função shortcode realmente é acionada. Portanto, a abordagem regex não é 100% confiável.
MikeSchinkel
Eu sei. Não uma maneira à prova de balas para injetar CSS para códigos de acesso (exceto usando <style>).
fuxia
Estou trabalhando com uma implementação baseada nesta solução e gostaria apenas de salientar que esse método não será colocado na fila para uma visualização de postagem / página.
mor7ifer
5

O Google me encontrou uma resposta em potencial . Eu digo "potencial", pois parece bom, deve funcionar, mas não estou 100% convencido de que é a melhor maneira de fazê-lo:

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

Isso deve ser capaz de verificar se a postagem atual está usando um código de acesso e adicionar uma folha de estilo ao <head>elemento adequadamente. Mas não acho que funcione para uma página de índice (ou seja, várias postagens no loop) ... Também é de uma publicação de 2 anos no blog, então não tenho certeza de que funcionará com o WP 3.1.X .

EAMann
fonte
Isso não funcionará se o código de acesso tiver argumentos. Se você realmente deseja seguir esse caminho, que é lento, use os WPs get_shortcode_regex()para pesquisar.
Onetrickpony
Como eu disse, a resposta "potencial" que eu não testei ainda ... :-)
EAMann
É basicamente o mesmo que o método 2, mas ainda não verifica o modelo para chamadas do_shortcode ().
Ian Dunn
Por que precisaria fazer isso? Se você ligar do_shortcode()manualmente no modelo, já saberá que estará executando o
código de
Não sou eu quem chama o shortcode, é o usuário. Isto é para um plug-in distribuído, não privado.
Ian Dunn
2

Usando uma combinação da resposta do TheDeadMedic e da documentação get_shortcode_regex () (que realmente não encontrou meus códigos de acesso), criei uma função simples usada para enfileirar scripts para vários códigos de acesso. Como o wp_enqueue_script () nos códigos de acesso é adicionado apenas ao rodapé, isso pode ser útil, pois pode lidar com os scripts de cabeçalho e rodapé.


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

Finalmente, também encontrei uma solução para carregamento condicional de css que funciona para o meu plugin www.mapsmarker.com e gostaria de compartilhar com você. Ele verifica se meu código curto é usado no arquivo de modelo atual e no header / footer.php e, se sim, enfileira a folha de estilo necessária no cabeçalho:

  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
fonte
um pouco de lado, mas isso não pressupõe que as pessoas estejam usando header.php e footer.php. E os métodos de agrupamento de temas, como os descritos em scribu.net/wordpress/theme-wrappers.html ? ou temas como raízes que mantêm suas partes de modelo em outro lugar?
Orionrush 03/03
1

Para o meu plugin, descobri que, às vezes, os usuários têm um construtor de temas que possui shortcode armazenado nos metadados pós . Aqui está o que estou usando para detectar se o código de acesso do meu plug-in está presente na postagem atual ou nos metadados da postagem :

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

porque CSS deve ser declarado dentro <head>

Para arquivos CSS, você pode carregá-los na sua saída de shortcode:

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

Defina uma constante ou algo depois disso, como MY_CSS_LOADED(inclua apenas o CSS se a constante não estiver definida).

Ambos os métodos são mais lentos do que seguir esse caminho.

Para arquivos JS, você pode fazer o mesmo se o script que estiver carregando for exclusivo e não tiver nenhuma dependência externa. Se não for esse o caso, carregue-o dentro do rodapé, mas use a constante para determinar se precisa ser carregado ou não ...

onetrickpony
fonte
3
Carregar CSS fora do <head>elemento não é uma marcação adequada. É verdade que a validação é apenas uma diretriz, mas se estamos tentando seguir essa diretriz, o carregamento da folha de estilo dentro da saída de código curto é uma má idéia.
EAMann
Blocos CSS embutidos são marcações válidas, mesmo em XHTML, pelo que me lembro. Não há nenhuma razão para não usá-los quando você não tem outra alternativa aceitável
onetrickpony
11
De acordo com a ferramenta de validação do 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". Portanto, os estilos embutidos ( <element style="..."></element>) são válidos, mas os <style>elementos embutidos não.
EAMann
11
torne-o filtrável e qualquer outro plug-in ou tema pode fazer o que quiser com ele. Se eles configurarem o filtro para retornar uma string vazia - nada será impresso.
mfields
11
Você não mencionou nenhuma razão objetiva contra essa prática. Enfim, não importa; Eu só vejo duas opções aqui: Sempre carga CSS / scripts (otimizá-los para o tamanho), ou estilos inline condicionais
onetrickpony
0

Script Logic é um plugin do WordPress que oferece controle total sobre todos os arquivos JavaScript e CSS. Usando este plug-in, você pode carregar condicionalmente os arquivos CSS e JS apenas nas páginas onde for necessário.

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

Tahir Yasin
fonte
Isso não é realmente relevante para esta questão, que consiste em controlar o enfileiramento de dentro do próprio plugin. Sua abordagem exigiria que o usuário instalasse outro plug-in e entendesse como configurá-lo corretamente.
Ian Dunn