Strategies for loading scripts in your HTML page

As developers we know that loading scripts makes our page render slowly. The problem with scripts is that they can call document.write(), which will potentially modify the DOM. That is why browsers must fetch the script and execute it right away, while the rest waits. One technique to cope with that is to move all your scripts to the end of your <body>. By doing this you let the browser render the entire HTML page along with the CSS styles first and then execute the scripts. This is only possible, of course, if your scripts do not make any changes to the DOM by calling document.write().

While we have already got used to this standard way of loading scripts, there are a couple of other options that one can consider.

Asynchronous loading

The <script> tag has an attribute, called async. By adding this attribute to your script tags, you instruct the browser to start loading the script and continue executing the rest of the page. This means there is no blocking. When the content of your script has been downloaded, the browser executes it. An interesting question is what happens if you decide to call document.write() from an async script. In the best case you will get a warning in the console. In the worst case… everything can happen 🙂 According to Mozilla’s documentation, calling document.write() in Gecko 1.9.2 has an unpredictable effect.

Another important note here is that the async attribute is part of HTML5 and some browsers are not really happy with it. Here is what Can I Use says about it:


Deferred loading

Another attribute that has been here for a while is defer. It acts partly like async in that manner that the browser starts downloading the script but continues parsing the rest of the page afterwards. The difference comes in when the script content is executed. While the async scripts are executed when the content has been downloaded (and actually it can be in any order compared to other async scripts), defer scripts are executed in the order of their definition on the page and when the page has been parsed. Note that the correct event here is parsed, not loaded. Moreover, one can attach defer even to inline scripts, i.e. scripts without a src attribute. The same has no effect with async. And here you can see the level of browser support:



AMD implementations like RequireJS rely heavily on the the possibility to download scripts asynchronously. This allows the browser to keep rendering the page while the scripts are being downloaded and we as end-users see (hopefully) a nicely designed page that instructs us that some loading is in progress (if necessary).


These two attributes gives us more freedom in the way we want to serve our scripts. If you obey the open-closed part of the SOLID principles and try not to interfere with the DOM directly, you will be able to make most of your scripts asynchronous. This problem is also solved by the AMD technique, where you can also keep track of your dependencies.