Faster page rendering with async, defer and preload
The browser performs a series of complex operations before you can actually view a page. In this post we look at how a page is rendered and the issues surrounding it.Lastly we find out how to overcome these issues with the help of simple tweaks.
Page Rendering
To render a page the browser converts the HTML in a format which is understood by it - Document Object Model(DOM). It does so with the help of a special functionality known as the parser. The parser converts the HTML into DOM.
The DOM is a tree like structure which depicts the parent child relationship between different elements on a page. It is built incrementally. As soon as a chunk of code is available, parsing begins and nodes are added to the tree.
Consider this HTML document:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Async and Defer</title>
</head>
<body>
<p id="one">This is the first paragraph</p>
<p id="two">This is the second paragraph</p>
<script src="main.js"></script>
</body>
</html>
The parser looks for tokens such as head, body, p, div, etc. for DOM construction. These tokens are then used to create the object or DOM node corresponding to the tag. The object contains the properties and rules of these tags/elements.
After parsing the HTML document from above the DOM tree would look like the diagram below.
CSSOM
Just like the DOM another model is generated by parsing the CSS . It is known as the CSS Object Model (CSSOM). This object contains the styling information associated with an element.
Suppose we apply the CSS rules shown below to our code from before.
p {
font-weight: bold;
}
#one {
font-size: 2rem;
}
It will generate the CSSOM as follows:
Let's also take a look at the DOM tree as seen in the Chrome dev tools.
!
In this image we can clearly see:-
- DOM tree showing the different elements and the parent child relationship.
- DOM node p with id "one".
- Its properties in the dev tools.
- Properties clearly show the value of id, innerText and innerHTML to name a few.
Now let's also see the CSSOM for the same example in the Chrome Dev tools
You can view the CSSOM properties for a node in the Styles tab of the dev tools.
- It will show you the different CSS rules which are applied on every element.
- In this image we can see the CSS rules which are applied to the p DOM node with id="one".
Both the DOM and CSSOM provide an interface using which we can read, style, modify or delete the contents of a page.
Let's see examples of how we can access the DOM and CSSOM using JavaScript:
// gets the element object(DOM node) whose id is one.
// access the innerText property on the element object to get the value
const text = document.getElementById("one").innerText;
console.log(text);
// Output => This is the first paragraph
// using CSSOM to change the text color
// access the style property and set the color to red
document.getElementById("name").style.color = "red";
Script Tag
The script tag in HTML allows you to add JavaScript code to your HTML document. You can either add a reference to a JavaScript file or you can add it inline.
<!-- external script reference -->
<!-- using src attribute to specify the path of the external script -->
<script src="path/to/main.js"></script>
<!-- inline script embedding -->
<script>
console.info("this is an inline script");
</script>
When the parser comes across a script tag, it pauses DOM construction. The control is handed over to the JavaScript engine, which then executes the script. After script execution, the parsing resumes.
Since the script tag pauses the parsing of the HTML, it is known as parser blocking.
In case of an external script, there is an additional step involved. The browser must first wait for the script to be fetched from a disk, cache or a remote web server.
Why is the script tag parser blocking?
- The script may contain code that can access DOM and modify it. This modification may have impact on future DOM construction. For example - an element is deleted.
- It can also access the CSSOM and modify the styling for an element.
While the DOM is constructed incrementally the same cannot be said about CSSOM. The browser performs complex calculations to apply styling from CSS to elements.
CSS rules can override each other. On top of that our script can also alter the styling of an element. Therefore the browser needs all the styling information(CSSOM) before hand.
Are you wondering what happens if the CSSOM isn't constructed at the time of script execution? The browser delays script execution and DOM construction until the CSSOM is constructed.
While CSS isn't parser blocking it will definitely block rendering of the page. Browsers don't render the page until the DOM and CSSOM are constructed.
These three aspects - DOM, JavaScript and CSSOM are interdependent on each other. DOM cannot be constructed completely until the JavaScript is downloaded and executed. JavaScript cannot be executed until the CSS is downloaded and CSSOM is constructed.
For a quick page render it is critical to manage these 3 aspects in an optimal manner.
Technically it is also known as the Critical Rendering Path.
Have you wondered why it is a best practice to keep styles in top and scripts at the bottom in a HTML document?
To make CSS available as soon as possible and CSSOM is constructed before scripts are executed.
Improving the performance
So far we've seen the various factors which contribute the initial page rendering and also the issues surrounding them. Now let's take a look at how we can improve the performance.
Preloading with rel="preload"
With the help of this attribute you can load more important resources earlier.
<!-- preload css -->
<link rel="preload" href="style.css" as="style" />
<!-- as attribute is used to specify the type of resource -->
<!-- loading javascript -->
<link rel="preload" href="main.js" as="script" />
Using the preload value in rel attribute of a link element you can inform the browser to fetch resources that your page will need very soon. It informs the browser to prioritise loading these resources earlier than others. By making them available earlier, these resources are less likely to block a page's render.
While preloading does allow you to prioritise loading of resources faster it still doesn't solve the issue of parser blocking scripts. Is there a way to overcome this issue?
Async and Defer
Async and defer are attributes on the script tag which allow a script to be fetched in the background without blocking the parser.
Async
This attribute informs the browser to fetch/download the scripts along with parsing. As soon as the script is available it will be executed. Parsing would only be blocked when the script is being executed.
<!-- usage -->
<script src="index.js" async></script>
You would generally use async for scripts which are not immediately required for the functionality of the website. This would include functionality such as analytics. While analytics is important, it is not critical for the user of the website. Therefore you can delay its execution.
Defer
This attribute indicates to the browser that this script needs to be executed after completion of parsing but before executing the DOMContentLoaded event.
DOMContentLoaded event occurs once all scripts with defer attribute have been executed, and there are no styles which are blocking JavaScript.
Scripts with the defer attribute are executed in the order they are written in the document.
<!-- usage -->
<script src="index.js" defer></script>
Both these attributes prevent parser blocking javascript. The difference between the two is the manner in which the scripts are executed.
In case of async, the scripts are executed as soon as they are downloaded. Whereas in case of defer, the scripts are executed once the document has been parsed but before the DOMContentLoaded event has been fired.
Note
This is a republish of my original article https://www.gauravsen.com/faster-page-rendering-async-defer-preload/ on my blog -https://gauravsen.com/blog
Video Examples
You can view this video on my YouTube channel which covers the same topic with detailed coding examples. It will deepen your understanding of the topic.
Faster page loading with async and defer!
Conclusion
In this article we learned about the various factors that play a key role in the rendering of a page. Minor changes to the way scripts are fetched and executed can have a large impact on the rendering of your website.
These simple features allow you to tweak up the performance of your website. Performance can even be enhanced for slow networks where the script/css file size may be large. Use the correct attribute based on the requirement to get the best results.
Interesting topic and well explained. Looking forward to read your next article.
Thanks Noor. Appreciate your feedback.