how and why to defer parsing of javascript in a webpage
Most modern day websites heavily use client scripts to make the websites dynamic, responsive and personalized. The most popular of these scripts is the JavaScript, which is used to display third party widgets, dynamic menus, responsive forms etc. to name a few.
These scripts however do have the potential to negatively impact the rendering speed of a webpage. This is mainly due to two reasons: the download time and the parsing time. In order for a script to be executed, it needs to be downloaded to the browser just like any other resource and then the file needs to be parsed by the browser before rendering.
In order to render a page completely, the browser will have to parse all the content of the <script/> tags, whether inside the HTML code or from the external js files. This can add valuable time to the rendering process as the rendering process needs to block, as all the script code is downloaded and parsed. From this "not so accurate" and vague description of the rendering process, you can deduce that there are mainly two broad ways to speed up the process…
Minimize Script Code: You can minimize the amount of JavaScript code used in the webpages. You should pay special attention the code that is required to render the page. Try to move the code that is not needed for rendering to another external file, if possible.
Defer Parsing of the Code: You can also defer the parsing of the JavaScript code that is not needed for rendering to after the page is fully loaded and rendered. Thus deferring the JS code has the potential to reduce the download size and allow other resources to be downloaded in parallel for faster speed.
Roughly, each kilo byte of JavaScript code adds another milli-second to the parsing and loading time of the page. This is mostly the parsing time, which means it happens every time a page loaded, even if the code is cached in the browser.
There are several different techniques that are used to defer the parsing process. Each of the techniques have varying levels of difficulty and effort involved, so your will need to understand how much bang each of these technique can get you for your particular use-case…
The primary task in all of the techniques is to differentiate between code that are required for rendering and those that are not required. It might be inevitable that some of the code need to be part of the rendering process such as those used to construct the page itself, but most of the code are usually functions that respond to user interactions such as clicks after the page has been loaded.
Event Listener
One technique used is to create a small JavaScript function that loads the script files only after the HTML page has been parsed and rendered. You can create a function which is an event listener that gets fired when the HTML has been loaded.
The below is a sample of how this could be done. You can find plenty of examples in various sites, and they all work using the same technique. You can place this code at the bottom of your HTML page, just before the ending body tag.
<script type="text/JavaScript"> function loadFiles() { var script = document.createElement('SCRIPT'); script.src = "my-javascript-file.js"; document.getElementsByTagName('HEAD')[0].appendChild(script); } if (window.addEventListener) { window.addEventListener("load", loadFiles, false); } else if (window.attachEvent) { window.attachEvent("onload", loadFile); } else { window.onload = loadFiles; } </script>
The advantage of this method is that it can work on most browser versions, even the older ones that may not support the async or defer options, which we will see next.
async Script Tag
Another option is to use the boolean attribute async in the script tag. This essentially states that the script file or files are not dependent on other resources, which means it can be downloaded in parallel with out blocking the HTML rendering and then executed as soon as it is available.
When async attribute is not present, the script is downloaded and executed immediately as soon it is found. This means it will block the parsing and rendering of the rest of the page as this is done.
When async attribute is present or set to true, the script files are executed asynchronously, so it could run in no particular order. It might be executed while the rest of the page is still parsing. And if there are multiple script files, there is no guarantee that it will be run in order.
The usage is very simple as shown in the example below…
<script src="/js/my-script.js" async></script>
defer Script Tag
defer is a boolean attribute that is supported in all modern browsers. When specified the script code is executed only after the entire page or the HTML parser has finished parsing. This attribute has an effect only for external scripts, which means the src attribute must be present as well.
When defer is not present, the script is or might be executed as soon as the script is downloaded. This could occur while the other resources in the page is still being downloaded.
When defer is specified or set to true, the script is downloaded asynchronously and executed only after the parser is finished. This can be useful if you have a large resource intensive script or if you need the page to be fully loaded before executing the script. Again the usage is as simple as the async attribute above…
<script src="/js/my-scripts.js" defer></script>
The defer also includes the async part in it, so it is not necessary to specify both the defer and async together. Although the practical use might defer with browser implementations. If a browser (as some legacy browsers) does not support one or the other, it can work as a graceful fallback.
To summarize, the effects of the async and defer on the script tag are
No tag attribute: The script is fetched and executed as soon as the parser encounters it in the page. The parsing is blocked while this happens.
async is specified: The script is fetched and executed asynchronously with the rest of the page. If there are multiple scripts, then the order of execution is not guaranteed. This is most appropriate for scripts that are not dependent on other scripts or resources in the page while rendering.
defer is specified: The script is fetched asynchronously but executed only after the HTML parser is finished. Unlike async, this ensures the order of execution of scripts. This is best suited for scripts that are dependent on the page content or DOM as you would want the page to be fully loaded before executing the script.
Using eval()
Another technique is to load the javascript code in such a fashion that it does not trigger the parsing or execution of the script. The script code is only parsed and executed if and when it is needed. There are a couple of different techniques that are used to load the script without parsing.
Comments: Popularized by Google's Gmail, the code is loaded within the comments of the page source. The browser will not parse it assuming it is nothing but comments, and when needed you can find the DOM element for the comments, strip out the comment blocks and run the code using the eval() method.
JS Variables: You can add the entire javascript module code into a javascript variable (or several variables) as a literal string. Again the string is then evaluated using the eval(), if and only when needed.
This technique is pretty involved which make appropriate for certain use cases. Most web application can be effectively managed with other techniques mentioned above. But, this is particularly useful for mobile applications.
Some mobile apps would want to download all the javascript code upfront, so that it can be used as and when needed with out any appreciable delay. The network reliability of mobile devices can be spotty and this technique provides a seamless experience. This is also true of applications that need to work offline.
This also reduces the overall page load time as it takes no time execute the javascript code while rendering. This savings from the non-parsing is useful when the code is only needed when a user performs a particular action, which they never might.
Script tag Location
Another popular method is to put the blocking tags at the bottom of the page, just before the closing body tag. This essentially ensures that code is fetched, parsed and executed only after all the other resources have been fetched. This is also useful when the code is mostly in-lined rather than an external javascript file (Remember that async and defer works with external files).
This has only limited success when compared to other methods. The page will still block and display as loading while the parsing happens. The only advantage is mostly visual, as the page content could be visible to the user while rest of the page is still loading.
This is a quick and easy trick when you do not want the hassle of implementing other advanced techniques.
Prevention is better than cure. When using third party code such as framework plugins, it usually has a lot of code for features that you probably is not using at all. That means if you can optimize the JavaScript code to the bare minimum that is required and is actually used, and that in many cases can be just as effective as trying to defer the parsing, executing asynchronously, downloading in parallel and all the other stuff….