有条件地为短代码加载JavaScript / CSS


37

我发布了一个创建简码的插件,该插件需要一个JavaScript文件和一个CSS文件才能在包含该简码的任何页面上加载。我可以在所有页面上加载脚本/样式,但这不是最佳实践。我只想在调用短代码的页面上加载文件。我发现有两种方法可以这样做,但是都有问题。

方法1在shortcode处理函数中将标志设置为true,然后在wp_footer回调中检查该值。如果为true,则用于wp_print_scripts()加载JavaScript。这样做的问题是,它仅适用于JavaScript,而不适用于CSS,因为CSS应该在内部声明<head>,您只能在类似initor 的早期挂钩中执行此操作wp_head

方法2提前触发并“向前看”,以查看当前页面内容中是否存在简码。我比第一种方法更喜欢这种方法,但是它的问题无法检测模板是否调用do_shortcode()

因此,我倾向于使用第二种方法,然后尝试检测是否已分配模板,如果分配了模板,则将其解析为简码。但是,在此之前,我想检查是否有人知道更好的方法。

更新:我已经将解决​​方案集成到了我的插件中。如果有人想看到它在实时环境中充实,可以下载浏览它

更新2:由于WordPress的3.3,它现在可以打电话wp_enqueue_script()直接在短码回调里面,和JavaScript文件将文档的页脚中调用。从技术上讲,这对于CSS文件也是可行的,但应将其视为一种不好的做法,因为在<head>标记外输出CSS 违反W3C规范,可能会破坏FOUC,并可能迫使浏览器重新呈现页面。


我过去使用过方法1的一种变体。在页脚中为短代码加载JS,在行内为短代码加载CSS。它可以工作,但是有点黑。期待更好的解决方案。
EAMann 2011年

内联CSS的主要问题是很难用其他样式覆盖,并且不使用wp_enqueue_styles()方法。
伊恩·邓恩

你有多少css?
mfields 2011年

多少行的javascript?
mfields 2011年

2
javascript是简单的部分。最好的方法是在“ Jedi”下:scribu.net/wordpress/optimal-script-loading.html
mfields 2011年

Answers:


11

根据我自己的经验,我使用了方法1和2的组合-1的体系结构和页脚脚本,以及2的“超前”技术。

对于前瞻,我使用regex代替stripos; 个人喜好,速度更快,并且可以检查“格式错误”的短代码;

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

如果您担心do_shortcode手动使用作者,我会选择指示他们使用操作调用来手动使您的预注册样式入队。

更新:对于从未使用过RTFM的懒惰作者,输出一条消息以突出显示其方式的错误 ;)

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

那是个很好的观点。理想情况下,我希望它可以在不需要他们做任何其他事情的情况下工作-因为一半的时间他们可能不会先阅读FAQ,因此他们只会认为它已损坏-但我最终可能会这样做。我可以在每个页面上注册脚本,但是只有在我检测到短代码时才将它们排队。然后,用户可以进入init并在需要的地方调用特定模板中的enqueue函数,前提是此时执行还不算太晚。另外,WP内置有get_shortcode_regex()。
伊恩·邓恩

3
如果用户擅长于实现do_shortcode(),那么假设他们同样擅长于遵循使短代码样式入队的说明是否合理?
Chip Bennett

1
的确如此,但是它将获得所有短代码的正则表达式,而不仅仅是您的;)“我可以在每个页面上注册脚本”可能也是更好的方法!请注意,他们不需要挂在init前面的任何地方wp_head。对于懒惰的开发人员,请检查wp_style_is( 'my_style_handle', 'done' )您的简码。如果为假,请打印出一个可见的错误,指导他们执行操作。
TheDeadMedic 2011年

@Chip-我不担心他们没有能力按照说明进行操作,只是他们不会知道应该这样做,因为99%的时间您不需要执行任何其他操作。
伊恩·邓恩

1
@Ian我的想法是,添加do_shortcode()到模板中已经在 “做一些额外的事情”-要做这些事情的用户可能已经知道需要加入样式,或者更愿意/可能遵循特殊说明。
Chip Bennett

8

我迟迟未回答这个问题,但是自从Ian从今天在wp-hackers列表上启动此线程以来,它使我认为值得回答,特别是考虑到我一直在计划将这样的功能添加到我一直在研究的某些插件中。

考虑的一种方法是检查第一页加载,以查看是否实际使用了短代码,然后将短代码的使用状态保存到后元密钥中。这是如何做:

分步操作方法

  1. $shortcode_used标志设置为'no'
  2. 在shortcode函数本身中,将$shortcode_used标志设置为'yes'
  3. 设置一个'the_content'挂钩优先级12,该优先级在WordPress处理完短代码并检查发布元数据后''使用键"_has_{$shortcode_name}_shortcode"''当帖子ID不存在帖子元键时,将返回的值。)
  4. 'save_post'如果用户更改了简码用法,请使用钩子删除帖子元,清除该帖子的持久标记。
  5. 还可以在'save_post'挂钩中使用,wp_remote_request()以将无阻塞的HTTP GET发送到帖子自己的永久链接,以触发首页加载和持久标记的设置。
  6. 最后一集'wp_print_styles',检查后为元的值'yes''no'''使用该密钥"_has_{$shortcode_name}_shortcode"。如果值是'no'不服务外部。如果值是'yes'''继续为外部服务。

那应该做到的。我已经编写并测试了一个示例插件,以显示这一切的工作原理。

插件代码示例

该插件会以一个[trigger-css]短代码唤醒,该代码会将<h2>页面上的元素设置为红色白色,因此您可以轻松地看到它正在工作。假定一个css子目录包含其中包含style.css此CSS的文件:

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

以下是工作插件中的代码:

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

屏幕截图示例

这是一系列屏幕截图

基本帖子编辑器,无内容

帖子显示,无内容

[trigger-css]简码的基本帖子编辑器

使用[trigger-css]简码发布显示

不确定是否为100%

我相信以上内容几乎可以在所有情况下使用,但是由于我只是编写了这段代码,所以我不能百分百确定。如果您发现无法使用的情况,我真的很想知道,这样我就可以修复一些我刚刚添加到其中的插件中的代码。提前致谢。


那么使用您的方法的五个插件会在每次保存帖子时触发五个远程请求?我宁愿在上使用正则表达式post_content。那么小部件中的短代码呢?
fuxia

@toscho触发后加载实际上是可选的;只是为了确保用户不必在加载了外部组件的情况下看到首页加载。这也是一个非阻塞调用,因此从理论上讲您不会注意到它。对于我们的代码,我们在基类中进行操作,因此基类只能处理一次。我们可以挂接该'pre_http_request'钩子,并在该'save_post'钩子处于活动状态时禁用对同一URL的多个调用,但是我要等到w真正看到了这一点,不是吗?至于小部件,可以增强处理能力,但这不是我所研究的用例。
MikeSchinkel 2013年

1
@toscho-您也不能确定短代码是否存在,因为另一个钩子可能会清除它。您可以确定的唯一方法是,是否确实触发了简码功能。因此,正则表达式方法并非100%可靠。
MikeSchinkel

我知道。有没有防弹的方式来注入的简码CSS(除使用<style>)。
fuxia

我正在基于该解决方案的实现方式,我只是想指出,此方法不会加入帖子/页面预览。
mor7ifer 2015年

5

谷歌搜索找到了我一个潜在的 答案。我说的“潜力”看起来不错,应该可以,但是我不是100%相信这是最好的方法:

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

这应该能够检查当前帖子是否正在使用简码,并将样式表<head>适当地添加到元素中。但是我认为它不适用于索引页面(例如,循环中有多个帖子)……它也是来自2年的旧博客帖子,因此,我什至不确定它是否可以与WP 3.1.X一起使用。 。


如果简码具有参数,则此方法将无效。如果您真的想以这种方式运行,这很慢,请使用WP get_shortcode_regex()进行搜索。
onetrickpony

就像我说的那样,我尚未测试过的“潜在”答案... :-)
EAMann 2011年

基本上与方法2相同,但是仍然不检查do_shortcode()调用的模板。
伊恩·邓恩

为什么需要这样做?如果您do_shortcode()在模板中手动调用,那么您已经知道您将在运行简码
onetrickpony

我不是打电话给简码的人,而是用户。这是用于分布式插件,而不是专用插件。
伊恩·邓恩

2

通过结合使用TheDeadMedic的答案和get_shortcode_regex()文档(实际上找不到我的短代码),我创建了一个简单的函数,用于使多个短代码的脚本入队。由于短代码中的wp_enqueue_script()仅添加到页脚中,因此这可以有所帮助,因为它可以处理页眉和页脚脚本。


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

最终,我还找到了条件CSS加载的解决方案,该解决方案适用于我的插件www.mapsmarker.com,我想与您分享。它检查我的简码是否在当前模板文件和header / footer.php中使用,如果是,则将所需的样式表放入标题中:

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

撇开一点,但这不是假设人们正在使用header.php和footer.php。那么scribu.net/wordpress/theme-wrappers.html描述的主题包装方法呢?还是像根这样的主题将其模板部分保留在其他位置?
orionrush

1

对于我的插件,我发现有时用户会有一个主题生成器,该主题生成器将简短代码存储在帖子元数据中。这是我用来检测我的插件简码是否存在于当前帖子或帖子元数据中的内容

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

因为CSS应该在里面声明 <head>

对于CSS文件,您可以在简码输出中加载它们:

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

设置一个常量或类似的东西MY_CSS_LOADED(例如(如果未设置常量,则仅包括CSS))。

两种方法都比这种方法慢。

对于JS文件,如果要加载的脚本是唯一的并且没有任何外部依赖关系,则可以执行相同的操作。如果不是这种情况,则将其加载到页脚中,但使用常量来确定是否需要加载...


3
<head>元素外部加载CSS 是不合适的标记。的确,验证只是一个准则,但是如果我们坚持该准则,则使在shortcode输出中加载样式表是一个坏主意。
EAMann 2011年

内联CSS块是有效的标记,即使我记得在XHTML中也是如此。当您没有其他可接受的替代方法时,没有理由不使用它们
onetrickpony

1
根据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"。因此,内联样式(<element style="..."></element>)有效,但内联<style>元素无效。
EAMann 2011年

1
使其可过滤,其他任何插件或主题都可以随心所欲地使用它。如果他们将过滤器配置为返回空字符串,则不会打印任何内容。
mfields 2011年

1
您没有提到反对这种做法的任何客观原因。无论如何都没关系;我在这里只看到两个选项:始终加载CSS /脚本(针对大小对其进行优化)或条件内联样式
onetrickpony

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.