Deferring JavaScript can dramatically speed up your website.

One of the toughest puzzles to solve, when trying to make your web pages load as fast as possible, is the recommendation from Google PageSpeed Insights that you should defer JavaScript in your web pages.

But before I go into the how, what you should do first is find out whether your website is already deferring its JavaScript or not.  You can find out by running the the test below.

Is your website deferring its JavaScript?

Find out whether your site needs to defer it's JavaScript, and if so, how much of an impact it can make.

If you’re already deferring your JavaScript, then kudos to you!

But if you’re not, then you’ll definitely want to keep reading, because what I’m about to share can shave seconds off of each page’s load time, which can improve your bounce rate and your search rank position.

What’s Going On When A Web Page Loads

When a web page loads, before the web browser can begin to display the page, any ‘render blocking resources’ must be first loaded — JavaScript is one of two types of render blocking resources (the other being CSS)  It “blocks” the “rendering” of the web page, until it is loaded.

Most people don’t notice this happening when they click on a link, until it takes longer than about 3 seconds.  But the reason that pages don’t load up as soon as the HTML for the page is loaded is usually due to render-blocking resources.

Makes sense, right?

So what happens if you have a lot of JavaScript loaded from different files?    JQuery? Bootstrap? A few jQuery libraries?  Some supporting JavaScript for in-page components?  Google Analytics?  Maybe a Google Map?

Many pages are jammed PACK with calls to all sorts of JavaScript files.

And, while the web page itself may be under 50KB, there can be over hundreds of kilobytes of JavaScript that BLOCK the page from rendering before they are loaded.

That’s a sure fired way of putting the brakes on a fast load time.

Unblocking the Blockers

There are a couple of different ways of unblocking JavaScript resources, but each come with their own issues.

The DEFER Method

The first method I’m going to talk about is the “DEFER” method. You could add the “defer” attribute to each of your external <script src=’…’></script> tags.

An example of what this would look like would be:

<script defer     src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>

What the ‘defer’ attribute does is tell the web browser to not load it until the HTML has finished loading.

This is actually one of the better methods, if you’re trying to satisfy the Google PageSpeed Insights tool. I’ll explain why in a little bit.

The ASYNC Method

The other common option is to add the “async” attribute to each of your external <script href=’…’></script> tags.  What the ‘async’ attribute does is tell the browser to start loading it immediately inline
(at the same time) with the loading of the web page, and then execute it as soon as it is loaded.

An example of what this might look like is:

<script async     src=”https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js”></script>

This method means that your JavaScript can possibly be loaded sooner than the DEFER method, however it runs into the problem that both the DEFER and ASYNC method are subject to, which can throw a monkey wrench in to your plans of easily deferring your JavaScript.

The Monkey Wrench

So, I’d like you to consider the following scenario in a web page that does not have any of its JavaScript deferred:

  1. jQuery is loaded
  2. Bootstrap is loaded
  3. Inline Block of jQuery that loads a bootstrap slider, that relies on jQuery is executed

In HTML, a streamlined version of your page would look like:

<html> <head> <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> <script type="text/javascript" src="https://netdna.bootstrapcdn.com/bootstrap/2.3.1/js/bootstrap.min.js"></script> </head> <body> <div class="tooltip" title="I love tooltips!">Guess what?</div> <script type="text/javascript"> jQuery(document).ready(function() { jQuery(".tooltip").tooltip(); }); </script> </body>
</html>

In the un-deferred version of the web page, everything is loaded in the correct order, and the tooltip initializes.  When the page is ready to be rendered, the text can be hovered to see the tooltip.

Now consider the following scenario in a web page that has the defer attribute applied to the external script calls:

  1. jQuery is loaded deferred
  2. Boostrap is loaded deferred
  3. Inline Block of jQuery that loads a bootstrap slider, that relies on jQuery is executed breaks because jQuery and boostrap is not yet loaded
  4. jQuery is actually loaded
  5. Bootstrap is actually loaded

In HTML, a streamlined version of your page would look like:

<html> <head> <script defer type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> <script defer type="text/javascript" src="https://netdna.bootstrapcdn.com/bootstrap/2.3.1/js/bootstrap.min.js"></script> </head> <body> <div class="tooltip" title="I love tooltips!">Guess what?</div> <script type="text/javascript"> jQuery(document).ready(function() { jQuery(".tooltip").tooltip(); }); </script> </body>
</html>

And in the situation where the async attribute is applied to the external script calls:

  1. jQuery is loaded asynchronously loaded [95.3K]
  2. Boostrap is loaded asynchronously loaded [35.9K]
  3. Inline Block of jQuery that loads a bootstrap slider, that relies on jQuery is executed breaks because jQuery and boostrap is not yet loaded
  4. Bootstrap is finished loading first because it is a smaller resource and takes less time to download
  5. jQuery is finished loading

In HTML, a streamlined version of your page would look like:

<html> <head> <script async type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> <script async type="text/javascript" src="https://netdna.bootstrapcdn.com/bootstrap/2.3.1/js/bootstrap.min.js"></script> </head> <body> <div class="tooltip" title="I love tooltips!">Guess what?</div> <script type="text/javascript"> jQuery(document).ready(function() { jQuery(".tooltip").tooltip(); }); </script> </body>
</html>

In the first scenario where we were deferring with the defer attribute, jQuery and Bootstrap were both loaded into the page in the correct order, but there was inline JavaScript that was dependent on them being pre-loaded into the page.

In the second scenario, where we were deferring with the async attribute, jQuery and Bootstrap were loaded out of order, as well as the inline JavaScript that was dependent on them being pre-loaded also could not execute.

What initially seemed like a good idea, either using the defer attribute, or the faster async method, ended up causing the page to break.

This is a very common scenario.

Solving the Monkey Wrench

There are three  possible solutions to the cart-and-horse situation imposed by the monkey wrench described above.

The “Grab It All, Shove it In the Footer” Method

This method is more or less exactly what it sounds like.  You would grab all of the JavaScript from all of your external resources and inline script calls, and in order place them within one giant inline <script>…</script> block in the footer of your page.
An example of what that might look like is:

<html> <body> <div class="tooltip" title="I love tooltips!">Guess what?</div> <script type="text/javascript">
/*! jQuery v3.2.1 | (c) JS Foundation and other contributors | jquery.org/license */
!function(a,b){"use strict";"object"==typeof module&&"object"==typeof... [this is a big file, so I'm only showing the first bits]
/**
* Bootstrap.js by @fat & @mdo
* plugins: bootstrap-transition.js, bootstrap-modal.js, bootstrap-dropdown.js, bootstrap-scrollspy.js, bootstrap-tab.js, bootstrap-tooltip.js, bootstrap-popover.js, bootstrap-affix.js, bootstrap-alert.js, bootstrap-button.js, bootstrap-collapse.js, bootstrap-carousel.js, bootstrap-typeahead.js
* Copyright 2012 Twitter, Inc.
* http://www.apache.org/licenses/LICENSE-2.0.txt
*/
!function(a){a(function(){a.support.transition=function(){var a=function(){... [again, another big file]
/* this is where your inline JavaScript would go */ jQuery(document).ready(function() { jQuery(".tooltip").tooltip(); }); </script> </body>
</html>

The downside to this method is that it makes your page itself much larger, by at least 200KB if you’re using a number of different libraries such as jQuery and Bootstrap.  If your page is already very large, this may not be the best option for you.

The upside to the “Grab It All, Shove it in the Footer” method is that if you have any images that need to be loaded, it gives the page enough time to finish its load, so that they aren’t jumping into view after the page loads.  Consequentially, by loading the HTML of the page too fast, while you have a lot of deferred resources loading at the end of the page asynchronously, you may end up getting penalized for not “Prioritizating Visible Content” by the Google PageSpeed Insights tool.  It’s related to the Flash of Unstyled Content.  Basically, the images in the visible region, of the page that is first loaded, don’t get enough time to load before the HTML is rendered.

The “Grab It All, Shove it In a File, Load it Asyncronously” Method

Again, much like the description, we do the same as in the previous example, but instead of “shoving it in the footer”, we save it to a file, and then somewhere near the footer of the page, we load it asynchronously towards the end of the page.

We do it towards the end (at the bottom of the HTML) because we don’t want the entire file to finish and then execute before the HTML Document is ready.  Otherwise, Javascript may try to execute looking for elements within the page that haven’t yet been allocated.

An example of how this would look would be:

<html> <body> <div class="tooltip" title="I love tooltips!">Guess what?</div> <script async type='text/javascript' src='/scripts/home-combined.js' ></script> </body>
</html>

The pro for this is that on secondary loads of the same page, the file can be cached and subsequently any JavaScript rendering that may happen happens sooner.

The con for this method is that if the page is very small, it may not have time to load any images within the visible region of the page that is initially loaded.  Basically, the does need to be some bit of render blocking (if only to get the foreground images loading).

On a side note, there is a way of in-lining foreground bitmap images, however it tends to bloat the size of the page dramatically — so I won’t cover that mechanism in this article.

The “Diligently Grab Inline Script and Shove it in an External File” Method

With this method, we add the keyword “defer” to each of the script tags on the page, and then bundle up any inline JavaScript from throughout the page, and put it into it’s own separate file.

We then reference that new file via a new script tag after the very last pre-existing external loading <script src=’…’></script> tag.  For example:

<html> <head> <script defer type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> <script defer type="text/javascript" src="https://netdna.bootstrapcdn.com/bootstrap/2.3.1/js/bootstrap.min.js"></script> </head> <body> <div class="tooltip" title="I love tooltips!">Guess what?</div> <script defer type='text/javascript' src='/scripts/home-inline.js' ></script> </body>
</html>

The reason we use defer rather than async is that we need to ensure that this is the very last item to load, in the event that it relies on code from previously called upon script resources.

The reason we use defer on the pre-existing externally loaded script calls, is that we need them all to load in the correct order.

A Side Note: The ONLY time to use async on a JavaScript call is if it is not depedent on any scripts and does not have any other scripts depending on it — basically, it’s okay to ASYNC a JavaScript resource if it is just a stand alone script.

The pros to this method are that if Browser Caching is enabled on the server, subsequent loads of the same page, or any other page that uses the same scripts, will be faster.

The cons to this method is that you may get a situation where the HTML renders too fast, and you end up getting penalized for not “Prioritizing Visible Content”.

But What About CMS Systems Like WordPress?

Yes, this is the sticky one.  Often, the only control you have over those is to dig into the code and set the script tags for every item to defer or load in the footer.  And you need to do that manually for every single Plugin and Theme that you have installed.

And then what happens if the plugin updates, or it has INLINE <script>…</script> blocks.  How are you going to handle that?

There is no easy answer to that, if you want to manually do these updates yourself to a WordPress website.

The only solution that we could come up with, was to build a plugin that programmatically looked at the entire page and applied the method from those solutions described above.

We built a plugin, called Pegasaas Accelerator, that defers all the render blocking JavaScript properly, on the fly.  It also defers the render blocking CSS at the same time.  It’s an amazingly powerful plugin, and it saves a ridiculous amount of time.

You can even try it for free, to see if it’s as awesome as we say it is.

Photo by Markus Spiske on Unsplash

Leave a Reply

Your email address will not be published. Required fields are marked *