Async and Defer Attributes

Summary

FooBox does not support the loading of its scripts using either the defer or async attributes. The short explanation is that FooBox relies on both inline scripts and has a dependency on jQuery and so can not make use of these attributes. The best possible performance on page load times for the plugin can be achieved by enabling the option to load FooBox's scripts in the footer of the page. This gives performance on par with the defer attribute and is supported across all browsers but it will not prevent FooBox's scripts being displayed in page speed optimizers as render blocking scripts.

Details

To explain in more detail you first need to understand what each of these attributes does and there effect on the scripts they are applied to. HTML5 supports both the defer and async attributes. These two attributes allow for a faster page load time but must be used correctly or else you may end up rearranging the execution of JavaScript code within your page resulting in errors.

Let’s start by explaining what a browser does when rendering a HTML file with a standard  <script> tag pointing to an external file. The HTML file will be parsed until the script file is hit, at that point parsing will stop and a request will be made to fetch the file. Once the external file is fetched the script will then be executed before parsing is resumed.

Next let's look at what happens when the async attribute is applied to a <script> tag. The HTML file will be parsed until the script file is hit, at that point a request to fetch the file is made but the parsing continues. Once the external file is fetched the HTML parser is paused and the script executed before parsing is resumed.

Then there is what the browser does when the defer attribute is applied to a <script> tag. The HTML file will be parsed until the script file is hit, at that point a request to fetch the file is made and parsing continues. Once the HTML parsing is complete then the fetched file is executed. defer scripts are also guaranteed to execute in the order that they appear in the document.

Finally there is a drawback to both async and defer, inline scripts. These attributes are ignored when applied to <script> tags that have no src. The HTML file will be parsed until the script file is hit, at that point parsing will stop and the script will be executed immediately as there is nothing to download.

Unfortunately this drawback with inline scripts causes an issue with a pattern adopted by many plugins, including FooBox. We include the core plugin JavaScript as an external file and then include the initialize code as an inline script that makes use of it. Basically it is something like the following.

<script src="foobox.js"></script>
<script>
    FooBox.ready(function(){
        // FooBox init code
    });
</script>

If you apply either the async or defer attribute to the external script file it is only loaded after the inline script has executed which then results in errors. As an example using the defer attribute; the HTML file will be parsed until the external script file is hit, at that point a request to fetch the file is made and parsing continues however it then immediately hits the inline script and parsing is paused while it is executed. At this point an error would be thrown by the inline script as the external file has not been executed yet. HTML parsing then continues and only once complete is the fetched external file executed.

So when should I use async and defer?

In the case of FooBox, do not use either but to summarize what has been explained above:

  • Use async if the external file being loaded has no dependencies. The reason for this is that as the script is executed immediately once it is downloaded the execution order is completely dependent on the size of the files and not the order they are included in the page. If your script has dependencies there is no way to be sure they are loaded at the time of execution.
  • Use defer if the external file has other external dependencies and no inline scripts. When using defer in this scenario the attribute should be included on all the dependencies and they should be included in the HTML in the correct order.

My optimizer plugin is adding async and/or defer to the FooBox <script> tag, how do I stop this?

Most plugins that apply async and defer also provide a means by which to exclude certain files as they understand you cannot simply apply them to everything and exclusions are required. Please refer to your specific plugins' documentation to find out how you can exclude the FooBox scripts.

Why can't you change FooBox to work with async and defer?

We fully intend to in the future, however as FooBox has been around for a number of years, since before the defer and async attributes were properly supported, we have options under our JS & CSS tab in the settings that would not be possible if we made the required changes to support the attributes. These options are used by numerous customers to apply customizations to their installs of the plugin and so removing them is not an option we are willing to make with the current version. This is an issue we are aware of and is something that we plan to rectify in the next major version change for FooBox when backwards breaking changes are expected and any current customizations would become invalid.