Wordpress - Enqueue Scripts / Styles when shortcode is present

I found an other way that works well for me:

  • When initializing the plugin, do not enqueue your scripts and styles, but register them with wp_register_style and wp_register_script.

  • Next you can load the script/style on demand. For example when you render a shortcode with wp_enqueue_style("your_style") and wp_enqueue_script("your_script").

Here is an example plugin using this method that lets you use get_avatar in a shortcode. The stylesheet is only enqueued when the shortcode is present.

Usage (id defaults to current user):

[get_avatar id="" size="32" default="mystery" alt="Profile Photo" 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' );
}

Before starting answer I have to say that regarding this topic css and js are not the same.

The reason is simple: while adding js to the body of the page (in footer) is a common and valid way to go, css need to be placed in the <head> section of the page: even if majority of browsers can proper render css in page body, that's not valid HTML code.

When a shortcode is rendered, <head> section was already printed, it means that js can be added without any problem on footer, but css must be added before the shortcode is rendered.

Scripts

If your shortcode needs only js you are lucky and can just use wp_enqueue_script in the body of shortcode callback:

add_shortcode( 'myshortcode', 'my_handle_shortcode' );

function my_handle_shortcode() {
  wp_enqueue_script( 'myshortcodejs', '/path/to/js/file.js' );
  // rest of code here...
}

Doing so your script is added to the footer and only once, even if the shortcode is used more than once in the page.

Styles

If you code needs styles, then you need to act before shortcode is actually rendered.

There are different ways to do this:

  1. look at all the posts in current query and add the shortcode styles if needed. This is what you do in both method #3 and #4 in OP. In fact, both two methods do same thing, but has_shortcode was added in WP 3.6, while get_shortcode_regex is available since version 2.5, so use get_shortcode_regex only if you want make your plugin compatible with older versions.

  2. always add shortcode style, in all pages

Issues

Issue with #1 is performance. Regex are pretty slow operations and launch regex in a loop of all posts may slow down page consistently. Moreover, it's a pretty common task in themes to show only post excerpt in archives and show full content with shortcodes only in singular views. If that happen, when an archive is shown, your plugin will launch a regex matching in a loop with the aim to add a style that will never be used: an unnecessary double performance impact: slow down page generation + additional unnecessary HTTP request

Issue with #2 is performance, again. Adding style to all pages means add an additional HTTP request for all pages, even when not needed. Even if server side page generation time is not affected, total page rendering time will, and for all site pages.

So, what does a plugin developer should do?

I think that best thing to do is adding an option page to plugin, where users can choose if the shortcode should be handled in singular view only or even in archives. In both cases is better to provide another option to choose for which post types enable shortcode.

In that way is possible to hook "template_redirect" to check if current query satisfy requirements and in that case add the style.

If user choose to use shortcode only in singular post views, is a good idea check if post has shortcode or not: once only one regex is required it should not slow down page so much.

If user choose to use shortcode even in archives, then I would avoid to run regex for all posts if number of posts is high and just enqueue the style if query fit requirements.

What to consider "high" on this regard should be get using some performance tests or, as alternative, add another option and give choice to users.


For my plugin I found that sometimes users have a theme builder that has shortcode stored in post meta data. Here is what I am using to detect whether my plugin shortcode is present in current post or post meta data:

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