Client-side performance can be dramatically improved by removing render-blocking JavaScript with LABjs. No more waiting and no more spinning-wheel icons for users to see. You can get a user speed experience of a simple site with all advantages of complex functionality-rich site.
Have you ever needed to boost up page speed? Has your boss asked you to improve SEO, PageSpeed ranking or client-side performance specifically? Modern websites contains a lot of JavaScript and that is the reason why Google PageSpeed tool provides you with hints such as
“Your page has blocking script resources. This causes a delay in rendering your page.“
Followed by description like this
“None of the above-the-fold content on your page could be rendered without waiting for the following resources to load. Try to defer or asynchronously load blocking resources, or inline the critical portions of those resources directly in the HTML.“
Remove render-blocking JavaScript:
https://[script1].js
https://[script2].js
...
Google now pays a lot of attention to blocking script resources recently. The reason is user experience. PageSpeed advises you to defer or asynchronously load blocking JavaScript, or inline the critical portions of JavaScript directly in the HTML.
There are a number of options available to those who want to speed the rendering of the page:
- use HTML <script> async attribute
- use HTML <script> defer attribute
- write custom JavaScript to do asynchronous load when the DOM is ready
- use third party library, probably even a Drupal contrib module (our choice)
However not all of those options have the same effect. Some of them may even behave differently than expected.
When we were faced with this situation we wanted our solution to have a cross-browser support and best client-side performance results possible. We nvestigated the options and ended up with LABjs.
LABjs (Loading And Blocking JavaScript library)
LABjs is a JavaScript loader, used by many large sites such as Twitter, Vimeo, examiner.com. It loads and executes all scripts in parallel as fast as the browser will allow. In other words, it is an on-demand parallel loader for JavaScript with execution order dependencies support.
Kyle Simpson, the author of LABjs puts it this way:
“LABjs is almost 4 years old, and has been stable (no bug fixes/patches) for almost 2 years.”
Quoting from LABjs library github page:
“The defining characteristic of LABjs is the ability to load all JavaScript files in parallel, as fast as the browser will allow, but giving you the option to ensure proper execution order if you have dependencies between files.”
LABjs library can be found on github: https://github.com/getify/LABjs. There is also a Drupal LABjs contrib module, https://www.drupal.org/project/labjs, which helps to integrate LABjs library to Drupal. It is not a big module, so fully comprehending what it does and verifying that it can be trusted wasn't hard.
After LABjs deployment on Morpht website, we received significant PageSpeed and YSlow rating boost and even more obvious user experience improvement. The site feels like it would be a simple website dispite the fact of being build on Drupal 7.
LABjs contrib module takes care of special cases and glitches like Google Analytics code or Advanced CSS/JS Aggregation module usage.
LABjs usage example
<script src="LAB.js"></script> <script> $LAB .script("http://remote.tld/jquery.js").wait() .script("/local/plugin1.jquery.js") .script("/local/plugin2.jquery.js").wait() .script("/local/init.js").wait(function(){ initMyPage(); }); </script>
First the LABjs library is loaded, then the global $LAB variable is used to schedule asynchronous defer loading of additional JavaScript files or inline code. All specified resources are downloaded asynchronously and their execution is deferred till the page has finished parsing. The specific order of resources execution can be required in some cases (quite often). The wait() function call expresses an execution order dependency, so all resources specified below the wait() function call will not be executed till the waiting resource has finished its execution.
DOM content load speed cut by 34%
We found a significant speedup in the loading of teh DOM content. The site we tested on was relatively simple and we would expect better results on more complex sites.
Before LABjs
- No asynchronous or defered JavaScript load is incorporated yet.
- DOMContentLoaded: 413ms
- Page load: 604 ms
After LABjs
- LABjs is being used, so asynchronous load and deferred execution are incorporated.
- DOMContentLoaded: 271ms
- Page load: 571ms
Be careful
Asynchronous load or defer execution of JavaScript requires some caution. The usage of LABjs or any other third party library is usually not enough. Additional code changes may be required.
Inline JavaScript code is not allowed to have any dependencies on any asynchronously loaded JavaScript file without proper synchronization. If you have such dependencies, they need to be taken care of first. The above-the-fold content can have JavaScript dependencies, so inlining critical portions of JavaScript resources directly may be required. As an example we can imagine JavaScript code required to render some graphics. More appropriate example could be an order button that needs to be active immediatly after page load, but its action requires JavaScript processing.
Other options
There are other options out there but they do have their shortcomings.
The HTML <script> async attribute can be used to asynchronously load JavaScript. Async attribute make the load of JavaScript to be asynchronous, but it parses and executes the JavaScript immediately after it is loaded. As a result the HTML parsing is being blocked. Plus the order of scripts exectuion is not guaranteed (first loaded is first executed). Full cross-browser support is not guaranteed.
The HTML <script> defer attribute can be used to defer the execution of JavaScript. Defer attribute causes the JavaScript to be loaded asynchronously and executed after the HTML parsing is completed. JavaScript is parsed immediately after it is loaded. Full cross-browser support is not guaranteed.
LABjs is cross-browser reliable. It parses and executes the JavaScripts after the whole HTML DOM is parsed (page loading icon indicates that the page is fully loaded already).
Bryan McQuade showed us how Google Developers eliminate render-blocking JavaScript and CSS in his video presentation.
Conclusion
You’ve just seen how to use LABjs library to turn the render-blocking JavaScripts into asynchronously loaded JavaScripts with deferred parsing and execution. As a real-life example the / website was presented. Its DOM content load speed was cut by 34%. That’s a good result for a site that uses JavaScript just sparingly. Imagine what could be accomplished on a complex website with a lot of JavaScript in use.
Remember that inlining just a critical portions of JavaScript resources directly in HTML and loading the rest asynchronously with postponed execution gives not only client-side performance boost, but also SEO boost that can make a difference.
My takeaway tip is to pay close attention to JavaScript code modularity so it can be easily separated based on the needs and use-cases required. Having good JavaScript means not only the functionality that’s needed, but also better user experience and better SEO ranking.
Helpful analytical tools
GTmetrix
- https://gtmetrix.com/
- Provides PageSpeed & YSlow reports together with Waterfall chart, page load time, total page size, number of request done and similar information.
PageSpeed
- https://developers.google.com/speed/pagespeed/
- Provides better mobile performance insights then GTmetrix.
YSlow
- http://yslow.org/
- Must be installed as a browser addon.
Appendix
There are terms, expressions and constructs that should be known before any action is taken to remove render-blocking JavaScript.
- Client-side
- Server-side
- Client-side performance
- Above-the-fold content
- Asynchronous JavaScript loading
- Defer loading of JavaScript
- Inlining the critical portions of JavaScript
- Client-side vs Server-side rendering
- HTML <script> async attribute
- HTML <script> defer attribute
Client-side
Client-side refers to operations that are performed by the client in a client–server relationship. In web application context it is a web browser, that runs on a user's local computer.
Client-side behavior forming user’s experience is significantly influenced by content received (HTML DOM), time interval between requesting and displaying page and code that needs to be parsed and executed (like JavaScript and CSS).
Server-side
Server-side refers to operations that are performed by the server in a client–server relationship. In web application context it is a web server, that runs on a remote server, reachable from a user's local computer.
Server-side is responsible for http response content. Most of the web-application logic usually resides on server-side. The speed and amount of processing required on the server-side together with the server’s connection speed contribute to the overall responsiveness of the web application.
Client-side performance
Page load time is a sum of time required to load page content with its all external resources and time required to parse and finally execute internal and external resources (like HTML, CSS, JavaScript). Most of current pages are using JavaScript. The degree of overall JavaScript usage on the site influences page load time.
Performance is a ratio of work that has been done and time it took. Client-side performance can be considered as a ratio of data size downloaded combined with all operations performed and page load time. Quite often client-side performance notion is being simplified to just a a page load time. It is quite fair from user experience point of view, so this simplification works fine.
The way how JavaScript is loaded, parsed and executed is one of the important factors that influences client-side performance as it significantly influences page load time.
Above-the-fold content
When you land on a website, “above-the-fold” is any content you immediately see without scrolling the page. The “fold” is where the bottom of the screen is. Anything you have to scroll down to see is “below-the-fold”.
“Above-the-fold” is a term typically used for Newspapers, content above where the paper is folded horizontally.
Asynchronous JavaScript loading
By default, JavaScript execution is “parser blocking”: when the browser encounters an inline script in the document or scripts included via a script tag it must pause DOM construction, hand over the control to the JavaScript runtime and let the script execute before proceeding with DOM construction.
However, in the case of an external JavaScript file the browser will also have to pause and wait for the script to be fetched from disk, cache, or a remote server, which can add tens to thousands of milliseconds of delay to the critical rendering path.
Inlining the relevant external JavaScripts can help to avoid extra network roundtrips.
Defer loading of JavaScript
The loading and execution of scripts that are not necessary for the initial page render may be deferred until after the initial render or other critical parts of the page have finished loading. Doing so can help reduce resource load burden and improve performance.
Quite often defer loading can mean that the resource is loaded asynchronously but the parsing and execution of that resource is deferred till the HTML DOM is loaded or page has finished parsing.
Inlining the critical portions of JavaScript
As a critical portion of JavaScript can be considered any JavaScript code that is required to make the “above-the-fold“ page content to look and behave as intended, immediatelly after the page is loaded. According to the principal of inlining, the amount of critical portion of JavaScript should be the bare minimum.
Extracting bare minimum of JavaScript to satisfy page needs immediatelly after load seems to not be a common practice yet. It is going to change and new approaches in JavaScripts development can help with that (components, typesafty, more structure and code separation, TDD, new JavaScript compilers like TypeScript and others: https://github.com/jashkenas/coffeescript/wiki/list-of-languages-that-co...).
Client-side vs Server-side rendering
Client-side rendering means JavaScript running in the browser produces HTML or manipulates the DOM.
Server-side rendering means when the browser fetches the page over HTTP, it immediately gets back HTML describing the page. It maintains the idea that pages are documents, and if you ask a server for a document by URL, you get back the text of the document rather than a program that generates that text using a complicated API.
Client-side JavaScript code should be optimized to be lightweight. The rule is to do as much on your server-side as you possible can. Such optimization can lead to significant improvement of page load performance.
It is even possible to render JavaScript templates on the server to deliver fast first render, and then use client-side templating once the page is loaded.
Exceptions can be found of course. Some calculations can be put to client-side JavaScript deliberately. The intention maybe to reduce server load for heavy operations that can be easily handled by JavaScript on background asynchronously to not impair page load.
HTML <script> async attribute
The definition of async attribute on w3schools.com is incorrect. It says:
“When present, it specifies that the script will be executed asynchronously as soon as it is available.”
But in fact what happen is that the async attribute tells the browser to not block the DOM construction while it waits for the script to become available. It means that the script is loaded/downloaded asynchronously, but when the loading is finished the HTML parser is paused to execute the script synchronously. This is still a huge performance win though.
There are few downsides
- Internet Explorer supports async attribute only from IE10 up.
- Ordering is not preserved when the async attribute is used.
- Any dependencies must be designed carefully. Synchronization could be required, as the exact timeline of operations and events is unknown upfront.
<script src="demo_async.js" async></script>
HTML <script> defer attribute
When the defer attribute is present, it specifies that the script is executed when the page has finished parsing.
Defer scripts are also guaranteed to execute in the order that they appear in the document.
<script src="demo_defer.js" defer></script>
Related videos
- "Optimizing the Critical Rendering Path for Instant Mobile Websites - Velocity SC - 2013" by Ilya Grigorik
Resources
- Client-side https://en.wikipedia.org/wiki/Client-side
- Server-side https://en.wikipedia.org/wiki/Server-side
- What is “above-the-fold content” in Google Pagespeed? http://stackoverflow.com/questions/18340402/what-is-above-the-fold-conte...
- Web cache https://en.wikipedia.org/wiki/Web_cache
- Remove Render-Blocking JavaScript https://developers.google.com/speed/docs/insights/BlockingJS
- Adding interactivity with JavaScript https://developers.google.com/web/fundamentals/performance/critical-rend...
- What are the tradeoffs of client-side rendering vs. server-side rendering? https://www.quora.com/What-are-the-tradeoffs-of-client-side-rendering-vs...
- async vs defer attributes http://www.growingwiththeweb.com/2014/02/async-vs-defer-attributes.html
- HTML <script> async Attribute http://www.w3schools.com/tags/att_script_async.asp
- HTML <script> defer Attribute http://www.w3schools.com/tags/att_script_defer.asp