Share

Updated: August 8th, 2010

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 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]')) {
			$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.

Our HP0-D07 study guides will help you to pass your 642-436 and 640-816 exam on first attempt guaranteed.

● ● ●

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/Bookmark

27 Responses to “[WordPress Plugin Development] How To Include CSS and JavaScript Conditionally And Only When Needed By The Posts”

    15 Comments:
  1. 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/

    • 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.

  2. gestroud says:

    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/

    • 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.

  3. Ken says:

    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.

  4. Lester Chan says:

    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

    • 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.

  5. jlapitan says:

    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…

    • 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.

  6. Otto says:

    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.

  7. 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.

  8. epsi says:

    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.

  9. Arvid says:

    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().

  10. Andrew says:

    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

  11. 12 Pings:
  12. Social comments and analytics for this post…

    This post was mentioned on Twitter by ArtemR: Just posted [Wordpress Plugin Development] How To Include CSS and JavaScript Conditionally And Only When Needed By …: http://ping.fm/DIM0S...

  13. [...] Dieser exzellente Artikel stellt eine verbesserte Lösung dieses Problems dar. Social [...]

  14. [...] This post was mentioned on Twitter by Donncha O Caoimh, wptavern, wptavern, andrea_r, ArtemR and others. ArtemR said: @weblogtooltips #Wordpress JS & CSS conditional include guide for developers: http://j.mp/5xa4bz Do you have any feedback? RT? [...]

  15. [...] Social comments and analytics for this post… This post was mentioned on Twitter by ArtemR: Just posted [ WordPress Plugin Development] How To Include CSS and JavaScript Conditionally And Only When Needed By …: http://ping.fm/DIM0S. … It is run by Artem Russakovskii – a local San Francisco geek who currently works at Plaxo and enjoys hacking Android, PHP, CSS, Javascript, AJAX, Perl, and regular expressions, working on WordPress plugins and tools , tweaking MySQL queries … See the original post:  [Wordpress Plugin Development] How To Include CSS and JavaScript … [...]

  16. [...] submission has been well received by plugin developers that have taken notice. The article explains how to include CSS and JavaScript conditionally so that the code is not loaded on every page of the site. If you think about it, there are many [...]

  17. [...] I'm wondering what your thoughts are on this article that other folks have looked highly upon. http://beerpla.net/2010/01/13/wordpr…-by-the-posts/ WPTavern Twitter Account | Personal Blog | WordPress Weekly [...]

  18. [...] nonsense.First of all, I'd like to thank everyone who read and gave their 2 cents about the [Wordpress Plugin Development] How To Include CSS and JavaScript Conditionally And Only When Needed … post. The article was well received and will hopefully spark some optimizations around loading [...]

  19. [...] How To Include CSS and JavaScript Conditionally And Only When Needed By The Posts [...]

  20. [...] tutorial on loading CSS and JavaScript and using WordPress queue functions for this. [Wordpress Plugin Development] How To Include CSS and JavaScript Conditionally And Only When Needed … VN:F [1.8.0_1031]Rating: 0.0/10 (0 votes [...]

  21. [...] Thanks goes to  Artem Russakovskii and his post on wordpress-plugin-development-how-to-include-css-and-javascript-conditionally-and-only-when-needed-by… [...]

Leave a Reply