Using Defer or Async with scripts in WordPress
How does a browser load and execute scripts?

Async or Defer loading Enqueued Scripts in WordPress

Age of Article Warning:
This article was originally published 41 months ago. The information, tips and techniques explained may outdated. Examples shown on this page may no longer work. Please consider this when viewing the below content.

Loading scripts and stylesheets is an integral part of the page loading for any website. These days we are well aware that web pages contain more interactivity, elements and scripts, accomplishing so much more than the early years when a website was just a simple HTML page (the very first website).

Page Loading Speed is Important

So how do we make sure that despite all the stuff that is going on while a webpage loads, the page still loads fast enough to prevent a viewer from impatiently “bouncing” away to another website. Well in WordPress we are encouraged to enqueue the scripts in the footer and load the stylesheets in the header.

The reason for this so that we want the web page’s visual elements to load first, so people see that the page is rendering. Otherwise a blank loading screen is a big turnoff.

The other important reason for “enqueuing” scripts into the footer is so that scripts can access the DOM page elements, which are often required as some scripts search for selectors on the page such as with jQuery.

How do scripts load normally on a web page?

So what happens as a page loads. Before I dive right in, I’d like to say that I am not an expert on this, so if I get something a little wrong, or a lot wrong, then leave a comment and explain it better for us all.

Ok so when the browser starts to render the page it parses the HTML. When a script is detected, the HTML parsing pauses while the script loads and executes. This means that if you have lots of scripts loading in the header, it holds up the page and so it may look like nothing is happening, and that might mean nothing for the viewer to look at!

Using Async or Defer

If we use the “async” option for loading a script file, then when the page loads, HTML parsing is not interrupted as the scripts are fetched (that is they load while the page continues to load), but once the script has finished loading it executes immediately.

While the script executes, HTML parsing is again stopped. However this still means a better user experience, and so using “async” is good for scripts that don’t depend on HTML elements loading first, or what order other scripts are loaded. So if you are loading a Google+ button then that’s fine to use “async” but if you are loading a slider that relies on jQuery script to have loaded first, then using “async” can cause some script issues.

An alternative, and the way that I prefer to load most of the scripts on a WordPress page, is to use “defer“. When you include “defer” in your enqueued script, then the scripts load at the same time that HTML parsing is happening. Then they wait until the page has finished loading, before executing. This means that the page is loaded first, then the scripts run.

What is also important is that they execute in the order they appeared. So if one script relies on another, such as those scripts that rely on jQuery, then your web page will still work the way you hoped!

This is what a defer script call looks like

 <script src="demo_defer.js" defer></script>

Not all is perfect in the browser world

Ok it’s important to add a warning here. Not always do things go the way they are supposed to. As you probably guessed – some browser versions do not play nice and can be erratic with how they “defer”. Thankfully though most modern browser versions do support “defer” correctly.

But always test yourself on different browsers, and if you notice any issues then inspect the javascript loading to see what is the culprit.

A defer code snippet for enqueued scripts in WordPress

The following function is what I use to add defer to enqueued scripts in WordPress. You will note below that I don’t defer for admin scripts, for jQuery itself, or for Internet Explorer 9.

//to add defer to loading of scripts - use defer to keep loading order
function script_tag_defer($tag, $handle) {
    if (is_admin()){
        return $tag;
    }
    if (strpos($tag, '/wp-includes/js/jquery/jquery')) {
        return $tag;
    }
    if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 9.') !==false) {
	return $tag;
    }
    else {
        return str_replace(' src',' defer src', $tag);
    }
}
add_filter('script_loader_tag', 'script_tag_defer',10,2);

For more reading please checkout the following resources:
growingwiththeweb.com/2014/02/async-vs-defer-attributes
developers.google.com/speed/docs/insights/BlockingJS

Print a nice copy of this page for reference

Filed under: WordPressTagged with: , , , ,

10 Comments

  1. where did you add this code?

    • Hi Ingo,
      you can add the function into your childtheme’s functions.php file.

  2. not working 🙁

    • Where in your functions.php file have you added the code?

  3. Thanks a lot! I didn’t know about the script_loader_tag-filter before 🙂

    • Yes it’s a relatively new WordPress hook since version 4.1, definitely useful. Thanks for the comment Jakob

  4. Hi,

    After using your code. I am getting an error in console as below:
    ———-
    “Failed to execute ‘write’ on ‘Document’: It isn’t possible to write into a document from an asynchronously-loaded external script unless it is explicitly opened.”

    maps.google.com/maps/api/js?sensor=true&ver=4.3
    —————

    Any solution?

    Thanks,
    Satya

    • Hi Satya,

      Thanks for letting me know of the issue with the Google Maps API and using my defer function above.

      It’s because document.write needs to fire before the page has finished loading, so it won’t work as expected. If you have the option to use the asynchronous version of the API then it should work.

      Otherwise you can simply skip deferring that script, by adding a check for it in the same way as the check for jQuery was done. So you could add something like this:

      if (strpos($tag, ‘//maps.googleapis.com/maps/api/js’) !== false) {
      return $tag;
      }

  5. Hey David,

    This is fantastic, the only thing google page speed doesn’t like about my site is JQuery and 2 other custom scripts being blocking rendering. So I am going to have to try and get this up and running properly.

    Have you found any way to only run functions after JQuery is loaded? Is this where loading JQuery in the head as async and then deferring the rest of the script is probably the best way to go? (sorry if this is a dumb question, I am fine with general coding of WordPress but Javascript and JQuery still get my head spinning).

    Thank you for a great article.

    • Thanks Henry for your comment. Since WordPress functions that enqueue scripts will usually have dependency set to jQuery if it is required for a script, you could possibly try to use async for jQuery and defer for the other scripts. However I’ve found it to be safer to allow jQuery to load early so that inline scripts and external scripts that are not enqueued, that sometimes need jQuery to not throw errors.


Comments are closed for this article!