Updated: August 30th, 2014

Introduction

wordpress logo In this tutorial, I am going to introduce a WordPress technique that I believe was unpublished until I raised the question a few days ago on the WordPress forums.

In short, the problem I was trying to solve was plugins unnecessarily loading their JavaScript and CSS on *every* page of the blog, even when doing so would achieve absolutely nothing and the plugin wouldn't do any work.

Update #1: I have posted a follow-up in response to some comments received around the web.

Update #2: There is a solution that can be considered a compromise as it works well for loading JavaScript but doesn't handle CSS.

I briefly mentioned this approach here but Scribu decided to expand on it by providing a nice Jedi-themed tutorial. It is available here.

Let me explain using this example:

  • a code formatter plugin only does something useful when it sees a [code] shortcode in any post on the page.
  • most of your posts do not contain the [code] shortcode as you don't include code snippets that often or you only started using this particular plugin recently.
  • the plugin, however, loads the CSS and JS (which are most likely GeSHi and take up loads of space) on every page.
  • these CSS and JS do absolutely nothing on most page loads.
  • bandwidth is wasted, extra DNS and HTTP requests are processed, the browser is slowed down, and for what? For no good reason, other than the plugin author didn't know how to achieve this conditional loading.

If you think about it, there are many plugins that only do something once in a blue moon. Table of contents, text manipulators, galleries, sliders, etc, etc. If only they loaded their frontend code strictly when necessary, most page loads would suddenly become much lighter.

So what can we do to solve this? Let's look at a few techniques.

Loading CSS And JS In Place?

Here's one, albeit pretty bad, solution – only print the CSS and JS includes if and when you determine somewhere in the middle of loading the posts that the scripts and styles are indeed needed, then set a flag to avoid printing them again. In our example, that would be when the plugin detects the [code] shortcode.

This, however, is a mediocre solution because, while it's not a bad idea to load Javascript in the footer, CSS should be loaded in the header, otherwise the page might look unformatted until the CSS is reached.

Additionally, it's not the cleanest and most robust solution because you shouldn't be writing <script> and <style> tags manually but rather using wp_enqueue_style() and wp_enqueue_script() functions.

What's This About wp_enqueue_FOO?

Now, you might say: "What's this about wp_enqueue_style(), wp_enqueue_script(), and then hooking into 'wp_print_scripts', 'wp_print_styles', 'admin_print_scripts', and 'admin_print_styles' hooks"? You know about these, right? Right??

Allow me to explain this, in my opinion, greatest and most underused WordPress paradigm in a short refresher:

WordPress has a great system of queuing up the scripts and styles your script will need to use and printing them all in one go, rather than hooking into wp_head and printing <style> and <script> tags manually.

This queuing system achieves 2 main goals:

  • the same scripts are not loaded multiple times, such as jQuery, scriptaculous, or any other custom script or style your plugins may share between each other or even other plugins.
  • it introduces support for establishing dependencies. You can specify that your script or style depends on another script or style, and WordPress will take care of loading them in the right order. Guaranteed.
  • oh what the hell, here's a 3rd one: elegance.

Here is the way this usually works:

  • in the beginning of your plugin you attach to wp_print_scripts and/or wp_print_styles hooks. For example:

    1
    2
    
    add_action( 'wp_print_scripts', 'enqueue_my_scripts' );
    add_action( 'wp_print_styles', 'enqueue_my_styles' );

    What this will do is call the functions in the 2nd parameter when it's time to execute any functions associated with the hooks in the 1st parameter. It is the main principle behind the WordPress plugin architecture.

  • looking at the script example, in the enqueue_my_scripts() function, you can do something like:
    1
    
    wp_enqueue_script( 'my_awesome_script', '/script.js', array( 'jquery' ));

    which would queue up your script to be printed later but only after jQuery.

    If you or some other plugin calls wp_enqueue_script() with the same first parameter (unique handle), it will just be ignored, rather than printed to the page twice.

  • styles are exactly the same, except you use wp_enqueue_style()

There is a variation of this functionality for the admin styles and scripts – all you have to do is change the hooks to admin_print_styles and admin_print_scripts. Ozh made a nice post on this topic here – check it out.

Enqueuing Alone Is Not Enough

Enqueuing is great for loading your JS and CSS but using it alone doesn't achieve the conditional behavior that we are looking for here.

This is a classic case of Chicken or the Egg, because WordPress makes only one pass through all of the content.

You see, since the header needs to be printed before the content, wp_print_scripts and wp_print_styles hooks are triggered before you even get to the posts. If you enqueue a script or style from within the 'the_content' hook, for example, the queued up scripts and styles will never get printed. It's too late to print them then anyway, as you're already in the middle of printing the posts.

The Solution

What we need to do is take a step back, before even printing the header, and then peek ahead.

Sure, this adds an extra pass over some data, but since no filters are applied during this process and if you avoid regular expressions (using stripos(), for example), this extra pass should be quite negligible.

A word of warning though: I'd rather see false positives (enqueuing when it's not needed) than false negatives (miss enqueuing when it's needed), so please do your matching wisely and test well.

The upside, however, can be potentially very substantial.

Credit goes to @white_shadow for the idea.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
add_filter('the_posts', 'conditionally_add_scripts_and_styles'); // the_posts gets triggered before wp_head
function conditionally_add_scripts_and_styles($posts){
	if (empty($posts)) return $posts;
 
	$shortcode_found = false; // use this flag to see if styles and scripts need to be enqueued
	foreach ($posts as $post) {
		if (stripos($post->post_content, '[code]') !== false) {
			$shortcode_found = true; // bingo!
			break;
		}
	}
 
	if ($shortcode_found) {
		// enqueue here
		wp_enqueue_style('my-style', '/style.css');
		wp_enqueue_script('my-script', '/script.js');
	}
 
	return $posts;
}

This simple function fires before the header gets printed, as it's attached to the 'the_posts' hook. However, this time it has full access to the posts' content.

I have tested this method and it works really well – if you have heavy scripts in your plugin, please do us, users, a favor and incorporate this logic into it.

Conclusion

In this tutorial, you have seen a method of loading scripts and styles for you plugin conditionally. This technique allows for less bloated pages and faster page loads.

Plugin developers, what is your take on this solution? Do you use another method? Please share in the comments.

● ● ●

Artem Russakovskii is a San Francisco programmer, blogger, and future millionaire (that last part is in the works). Follow Artem on Twitter (@ArtemR) or subscribe to the RSS feed.

In the meantime, if you found this article useful, feel free to buy me a cup of coffee below.



Share
  • http://mfields.org/ Michael Fields

    Hi Artem, Great article! This topic is very important and should be addressed by everyone extending WordPress. One thing that I noticed was that – In the function relating to shortcodes – was that this line:

    if (stripos($post->post_content, '[code]'))

    is restrictive in that it will only match when the shortcode has no attributes. I have outlined the following solution which matches a unique html comment instead:

    http://mfields.org/2009/12/13/how-to-create-a-wordpress-plugin-that-only-prints-css-for-certain-posts-pages-or-attachments/

    • http://beerpla.net Artem Russakovskii

      Michael, thanks for chiming in.

      I deliberately included a simple example that only matched [code] as I also wanted to avoid using regex, for speed purposes.

      Obviously, the plugin developer will want to use their own pattern matching and this is where my warning comes into play. I'd rather see them match on "[code" instead and potentially overinclude than miss one and not include.

      I looked at the functions in the shortcode API and what I was looking for is an easy way to see if a shortcode is present in a string. There is no clear function that would do that, and the one that is close is marked as private, which may mean it might actually become private once WP fully goes PHP 5, so I wouldn't dare use it. Plus it uses regex.

      I think the key here is balance – try to balance out the extra step involved with how much you're saving by not including extra files.

  • gestroud

    I found this post via your link on Weblog Tools Collection. It would make an excellent guest post there. Why not contact WLTC's owner and ask if it can be used as a Guest Post?

    http://weblogtoolscollection.com/contact-me/

    • http://beerpla.net Artem Russakovskii

      gestroud, thanks, yeah I did contact Mark but since I already posted it here, I would need to modify it enough to be unique again, which is not going to happen. If he feels like linking here at some point, however, I will welcome it.

  • Ken

    It's also good to note that in HTML5 you are explicitly allowed to use style tags in the body, and there is a new attribute, scoped, to apply the styles only to the parent element and its descendants.

    This is kool, but not yet implemented, but if you are using HTML5 early, you can now include styles in the content legitimately.

    • http://beerpla.net Artem Russakovskii

      Ken, that may be, but it still won't help the problem of the page loading without CSS until the CSS part is reached. That's why it's recommended to put it in the header.

      • Balthazar

        Maybe I'm being nitpicking but I don't like that idea…!! Wasn't the whole point of css to keep visual formatting separate from the contents?? I think allowing style tags in the body like that, sounds like (partially) giving up on that purpose… Or did I misunderstand something?

        • http://beerpla.net Artem Russakovskii

          We're not talking about putting the actual styles into the body, just a reference to the external style sheet.

  • http://lesterchan.net Lester Chan

    Nice post, maybe you should add in the codex.wordpress.org as well. The only concern I have is that it may require some processing power for sites which have lots of post because it is looping through all the post and the post content to find a match. This will result in more memory needed

    • http://beerpla.net Artem Russakovskii

      Thanks for stopping by, Lester.

      I do give a warning about the extra pass but if crafted carefully, it should be quite harmless. Additionally, it actually shouldn't use extra memory (except for a few KBs) because it's looping through already existing post data and not modifying anything.

      Also, I went ahead and edited a few codex pages to include info related to this post. Thanks for the suggestion.

  • http://www.jlapitan.com jlapitan

    this is a very nice article.. saw it on tweet by @themelab..

    why not try nettuts.com

    maybe you can write or rewrite this article.. 150$ each approved topic…

    • http://beerpla.net Artem Russakovskii

      Hey j,

      Are you affiliated with NetTuts? As I mentioned before, I don't want to rewrite an article just so it could be included on another blog.

      I will give them full rights to repost it though, or post it as a guest blogger, or just link to it.

  • http://ottodestruct.com Otto

    Using the_posts filter to look ahead is clever, but has some drawbacks.

    Mainly, the_posts happens before the wp_head call, but *only* for the default query. If I change my query later by using wp_query, or if I create other separate WP_Query's later, then the_posts will happen again, with different content, perhaps after the wp_head.

    Generally speaking, it's actually better to NOT optimize in this case and simply include your script all the time. Why? Browser caching.

    If we assume that your visitor is going to see at least one page that requires the script, then at some point he'll have to load the script into the browser. But that only happens one time, the rest of the time the script is already in their browser cache. So how much time do you really save them from doing this conditional loading? They have to load the thing anyway, generally. All that adding this conditional loading really does for you is to add a bunch of PHP execution in order to save some time that really isn't saved…

    This is unnecessary optimization. It's probably a de-optimization on most setups. I'd avoid doing lookahead unless you have a specific use case where it fits. It doesn't fit the general case.

    • Andrew

      @otto – I get what you're saying with some scripts but for the most part pages don't need the bloat. For example, lets say I have a calendar plugin on my site which is rather resource intensive. I only use the plugin on one page (calendar.php) and only those visitors with access can get to that page – but all the js and css files are loaded on every page anyway. That is an unneeded load for the bulk of my visitors.

      Also, browser caching is only really helpful for those visitors who want to navigate all around my site. What if I have one call to action page – and have that page linked from several out-bound sources. I want the load time of that page to be optimal more than I care about the load time for the other pages of the site which they may or may not visit, right?

    • Tobias

      Otto,

      "Generally speaking, it's actually better to NOT optimize in this case and simply include your script all the time."

      A number of, say, gallery scripts need explicit IDs in their code to be able to have mutliple instances on the same page, which requires counting the number of shortcodes.

  • http://contentsynergy.com Phil Barnhart

    While this is an interesting approach with several real-world applications, I am concerned about attempting this approach while ignoring appropriate cache control settings. When properly configured, your server should never send JS and CSS files on every page request. There are several excellent reviews on setting appropriate expires and cache control settings via PHP and on Apache (through config files and .htaccess). Properly compressed – and combined and optimized if needed – you should never need to load the same file continously. In fact, if you use the Google-sourced jQuery and other libraries, some of the largest files may already be cached on the users browser.

  • http://iluni.org epsi

    Why not using php output buffer 'ob_get_contents()' for main content in template?

    Steps:
    1. Run main content in output buffer, e.g. $ob
    2. wp_head() somewherein your theme, e.g. in htmlhead.php
    3. echo $ob

    This will run every enqueue scripts/styles before the wp_head.

    While this approach needs template overhaul. This makes template cleaner.

    • http://beerpla.net Artem Russakovskii

      ob_get_contents() is notoriously memory intensive and also slow, but yeah, the main point is that you can't write plugins for others to use like that because it requires a theme hack.

  • Arvid

    Great post! Exactly what i bredes for a plugin in development. Didn't get it to work with stringpos() though, had to use preg_match().

  • Andrew

    I am not having any luck with wp_enqueue_scripts when placed in a widget, even if in a function called through init – any suggestions?

    Andrew

    • http://beerpla.net Artem Russakovskii

      It looks like WordPress is doing this intentionally. See this bug: http://core.trac.wordpress.org/ticket/9346

      The solution outlined there (manually hooking into the wp_footer hook) works fine but is annoying and superfluous.

  • Sean Carmody

    I have just produced my first WordPress plugin and put your suggestion here to good use! Thanks very much.

    • http://beerpla.net Artem Russakovskii

      Awesome! Thanks for the credit.

  • Rickard Andersson

    Hi.

    Thank you for this post, it really helped me in plugin development. However I've found a little bug that I've corrected to get this to work properly.

    The bug appears when the shortcode is at the very beginning of the post, when stripos() returns zero it will be interpereted as false with the current if statement.

    To get this to work as expected, just change the if statement to:

    if (stripos($post->post_content, '[code]') !== false)

    Hope this will help others facing the same problem.

    • Saroj

      Thanks Rickard, now its working

  • Brian Gottier

    I've been using this code inside a jwplayer plugin that I created, and it does work well. There is one thing that I wish to figure out though. If there is no shortcode in the_posts, but there is one in a text widget, then how can I add js to the head? I looked at global variables to search for my shortcode, but I don't see a way to find it before wp_head.

  • David F. Carr

    I've published my variation on this pattern here, using the 'wp' action event to trigger javascript output for only a page with a designated shortcode.

    http://www.carrcommunications.com/2010/11/add-javascript-to-one-page-only-in-wordpress/

  • Judy

    Hey – thanks for the article. I will stop back for the coffee donation once I get things going! I'm on my first plugin, and really want to load selectively, in regards to admin pages.

    I have my js loading only on the page I am interested in by using admin_print_scripts-, but am having issues with figuring out how to include a necessary onLoad for the admin page.

    At any rate, the last paragraph of your blog currently states:

    "There is a variation of this functionality for the admin styles and scripts – all you have to do is change the hooks to admin_print_styles and admin_print_scripts. Ozh made a nice post on this topic here – check it out."

    I followed the link, but maybe things have changed? I cant find a discussion on admin_print_scripts there

    Best Wishes!

    • Judy

      Got it. I used a window.onload in my enqueued regular script. Would have preferred to do another way, in case I use JQuery later, but this works for now

  • Eric

    Hi,

    I had the same idea too but I hooked the 'wp_head' function instead of 'the_posts'.

    Anyway, Do you have any idea how to detect shortcodes in widgets?

    Cool Stuff

    • http://beerpla.net Artem Russakovskii
      • Eric

        Uh nope thats not it. I was hoping theres something I can do to detect the shortcode on the widgets so I can queue scripts and styles for a specific page only.
        Can the widgets be queried? I think thats the missing item to my puzzle here.

        • http://beerpla.net Artem Russakovskii

          I don't have time to look at the code right now, but I believe as long as the widget provides a hook you can hook into, you can get this information. Otherwise, you're out of luck. PHP Exec widget provides such a hook, but other widgets don't have to and don't – they just write directly to the page.

  • Rusty

    Thanks for this, I'm kind of surprised that this functionality isn't supported by WordPress natively.

    For my plugin I'm working on at the moment, other plugins can plug into it. Since I can't know what shortcodes other plugins will use when calling my plugin functions I wrote a little helper function that other plugins can call with their shortcode tag.

    I probably won't actually use it, and haven't really tested it, but may be useful to someone (maybe even myself in the future).

    Not sure if WordPress will mash up the code in this comment:

    	/**
    	 *Adds a script to the page <head>
    	 *@param string $handle The handle of the script to add, previously defined using wp_register_script
    	 *@param string $tag The shortcode tag name that should be present for the script to load
    	 */
    	public function enqueue_script($handle, $tag){
    		static $handles = array();
    		if(isset($handles[$handle])){
    			return;
    		}
    		$handles[$handle] = true;
    		add_filter('the_posts', create_function('$posts', '
    		if (empty($posts)){
    			return $posts;
    		}
    		foreach ($posts as $post) {
    			if (strpos($post->post_content, "['.$tag.'") !== false) {
    				wp_enqueue_script("'.$handle.'");
    				break;
    			}
    		}
    		return $posts;'));
    	}

    • http://beerpla.net Artem Russakovskii

      Looks like it worked just fine – thanks :) You could have alternatively used something like tinypaste.com for this in case it didn't work.

  • sarah

    Great read. I found your site on bing and i have your page bookmarked on my favorite read list!
    I’m a fan of your site. Keep up the good work

  • Tim

    Hi,

    Thanks for this awsome post! Bookmarked it :)

    I know i'm a bit late to the discussion, but i've got a question (i've read the updates and other solutions), but bear with me as i'm no WordPress guru. So my question might a nubish.

    Since i want to only print my stylesheet in the Front End where my shortcode is found, i would like to use this solution. Problem is that now my stylesheet is also loaded on every page in the backend… can this be prevented?

  • amitpatil.me

    how can i use custom javascript in plugin ??

  • Roger

    Be aware that if your post contains just "[code]" and nothing else, then

    stripos($post->post_content, '[code]')

    will return 0 (because the string is at position 0) which is evaluated to false. Try the following instead:

    if (stripos($post->post_content, '[code]') === false) {
    $shortcode_found = false;
    }
    else
    {
    $shortcode_found = true; // bingo!
    break;
    }

    • yzlow

      this is very helpful and extremely important. webmaster, please correct your post.

      • http://beerpla.net Artem Russakovskii

        I've updated the snippet.

  • Ian Dunn

    The problem with this method is that it doesn't detect when people call do_shortcode() inside a template rather than inserting it into a post.

    • Ian Dunn

      I found a way to deal with that problem. There isn't a practical way to detect if do_shortcode() is being called, so inside the shortcode handler you can just check if the scripts/styles have been enqueued. If they haven't then output an error message so that the theme developer knows they can't call do_shortcode() without also enqueueing the scripts manually, or telling the plugin which pages to load them on.

      I've written a more detailed explanation along with some code samples at http://iandunn.name/conditionally-loading-javascript-and-css-in-wordpress-plugins/.

  • Ronak

    Great article. Thank you so much.

  • สนามฟุตบอล

    loosely based on a post about conditionally loading JavaScript and CSS by Artem Russakovskii. He has a somewhat more com

  • สนามฟุตบอล

    the only issue is that is seems to remove the new line to BR, so all th eformatign goes.

  • The-Di-Lab

    In official WordPress codex, it says "Do not use wp_print_scripts.".

    http://codex.wordpress.org/Function_Reference/wp_enqueue_script

    But I see you are using this action over and over, can you explain why? Or it is a version sensitive issue?

  • randy4

    Thanks for sharing your website with all of us, rather informative

  • 荒野无灯

    great idea.I've translate this article to my language and modify some plugins I've used now.

  • Philip

    I was having a similar issue the other day, and all the existing plugins sucked, so I decided to write my own. I hope this helps someone!

    http://philipwalton.com/2011/09/25/per-post-scripts-and-styles/

    • Matth

      Phillip – this is awesome! Should save a lot of work and speed up the site quite a bit. Thanks for sharing your work!

  • adrian

    I would consider any such method that slims down the content delivered to all users, particularly at what seems like a small cost.
    But it gets better. Given that anyone interested in performance installs caching of one sort or another, the overhead for your excellent method is near negligible.
    The more users per cached content the better.

  • Matth
  • dentists london

    A really helpfull post – A big thank you I hope you dont mind me blogging about this post on my website I will also leave a linkback Thanks

  • CP Treasures

    I really respect the opinion of the writer here. This article is thought provoking, to say the least…

  • bfintal

    WP 3.3 can now allows the calling of wp_enqueue_scripts() inside an add_shortcode()

    • bfintal

      Typo. WP 3.3 now allows*

  • Simon Hayre

    great article, I needed a way to redirect the user if they didn't have permission to view a page which had a short code in the post. I couldn't do it in the body as wordpress had already written header data. but using your example allowed me to redirect the user. cheers arteem!

  • Robert

    Finally I also found a solution for conditional css loading which works for my plugin http://www.mapsmarker.com and I´d like to share with you. It checks if my shortcode is used within the current template file and header/footer.php and if yes, enqueues the needed stylesheet in the 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' );

  • Onefineham

    Thanks for this… it bleepin' worked!

  • Anisur Rahman

    It is a helpful post for plugin development.

  • Valentin

    The solution works just well! Thanks for your time.

    There's one small thing.. it loads the JS/CSS files when you edit the post in wp-admin and your css/js could break the admin.

    I'm sure there's a better way to do this, but here's my solution:

    if (empty($posts)) return $posts;

    if(stristr($_SERVER['REQUEST_URI'], 'wp-admin')) return $posts;

    Thanks again!

  • http://religionatthemargins.com/ Thom Stark

    This doesn't work for custom post types.

  • san go

    thanks for sharing!

  • Pingback: Add custom javascript files to the wordpress site | PHP SNIPPETS

  • Pingback: How to create plugin in WordPress

  • http://www.quangcaomedia.com duong

    thank for post

  • Pingback: Basic WordPress Pligin | Knowledge Base

  • Pingback: Writing a Plugin for Wordpress | ShowMEmn Voice

  • Pingback: Create a Plugins | For Web Development and designer Experience

  • Pingback: پیشنهاداتی برای توسعه و بهبود پلاگین | طراحی پوسته وردپرس، طراحی قالب وردپرس،طراحی سایت، طراحی وب سایت