When does parsing HTML DOM tree happen?
TL;DR: Parsing starts instantaneously after receiving the document.
Parsing and painting
For a more detailed explanation, we need to dive into the way rendering engines work.
Rendering engines parse the HTML document and create two trees: the content tree
and the render tree
. A content tree contains all DOM nodes. The render tree contains all styling information (the CSSOM
) and only the DOM nodes that are required to render te page.
As soon as the render tree has been created, the browsers goes through two processes: applying layout
and painting
each DOM node. Applying layout means calculating the exact coordinates where a DOM node should appear on the screen. Painting means actually rendering the pixels and applying stylistic properties.
This is a gradual process: browsers won't wait until all HTML is parsed. Parts of the content will be parsed and displayed, while the process continues with the rest of the contents that keeps coming from the network.
You can see this process happening in your browser. For example, open the Chrome Developer Tools and load a site of your choice.
After recording activity in the Network
tab, you'll notice that parsing starts while downloading the document. It recognises resources and starts downloading them. The blue vertical line indicates the DOMContentLoaded
event and the red vertical line indicates the load
event.
Recording a timeline gives you much more insight in what happens under the hood. I have included the screenshot above as an example to indicate that painting happens while parsing the document. Note that the initial paint occurs just before it continues parsing another part of the document. This process continues until it reaches the end of the document.
Single threaded
The rendering engine is single threaded. Almost everything, except network operations, happens in this thread.
Combine that with the synchronous nature of the web. Developers expect <script>
's to be parsed and executed immediately (that is: as soon as the parser reaches a script tag). That means that:
- The resource must be fetched from the network (this might be a slow process due to DNS lookups and speed of connection).
- The content of the resource is passed to the Javascript interpreter.
- The interpreter parses and executes the code.
Parsing the document halts until this process finishes. You don't improve the total parsing time by including <script>
's at the end of the document. It does enhance the user experience, as the process of parsing and painting isn't interrupted by <script>
's that need to be executed.
It is possible to work around this issue by marking the resource with defer
and / or async
. async
downloads the file during HTML parsing and will pause the HTML parser to execute it when it has finished downloading. defer
downloads the file during HTML parsing and will only execute it after the parser has completed.
Speculative parsing
Some browsers aim to work around the blocking aspect of <script>
's by using so called speculative parsing. The engine parses ahead (and runs the HTML tree construction!) while scripts are being downloaded and executed. Firefox and Chrome use this technique.
You can imagine the performance gain if a speculation succeeds (eg. the DOM wasn't altered by the scripts that are included in the document). Waiting for the scripts to execute wasn't necessary and the page has been painted successfully. The downside is that there's more work lost when the speculation fails.
Luckily for us, very smart people work at these technologies, so even using document.write
properly won't break this process. Another rule of thumb is not to use document.write
. For example, it could break the speculative tree:
// Results in an unbalanced tree
<script>document.write("<div>");</script>
// Results in an unfinished token
<script>document.write("<div></div");</script>
Further reading
The following resources are worth your time reading:
- Web Fundamentals (Google Developers)
- How browsers work
- MDN on HTML5 Parser
- An introduction to Browser Rendering (video)
This is actually dependent on the browser as to the specific order that everything is loaded but for DOM parsing it works top to bottom. The parser moves branch by branch, so when it encounters the head, it will move through each child. If an element has a child it will then move onto the child/children before moving back up the tree. To put that in very basic pseudo-code:
while DOM != parsed:
if current_node.has_child():
current_node = child_node
execute_node()
elif current_node.has_sibling():
current_node = sibling_node
execute_node()
elif current_node.has_parent_sibling():
current_node = parent_sibling
execute_node()
else:
current_node = parent_node
It essentially deals with script/link tags as parent nodes, initiate a HTTP/S GET Request if it's an external file and parse the code before moving on to the next node. So the reason we use put script tags at the end is because they usually aren't used at page load, but handle things after it is loaded. So the consensus is that it's better to get something on the page then load your JS later so that it can handle that oh so important animation you have on a menu item.
There are of course exception to this, whereby you can specify to the DOM parser to execute scripts asynchronously - the parser creates an extra thread to parse the JS - or defer - GET Request is made but the file is not parsed until the HTML Document is finished parsing.
Another saying is, putting
<script>
in the end of<body>
is the best practice so that the page renders something before script is downloaded.
The major reason of putting script tag to the end of body tag is: download and execute JavaScripts will block HTML parsing (or, you can say they are just parts of parsing). If they are put in the <head>
, the user may wait for a long time before he could see anything on web page. Image you have a html page like this:
<html>
<head>
<!-- this huge.js takes 10 seconds to download -->
<script src="huge.js"></script>
</head>
<body>
<div>
My most fancy div!
</div>
</body>
</html>
// huge.js
(function () {
// Some CPU intensive JS operations which take 10 second to complete
})();
The browser will start to execute those CPU-intensive JS right after it reaches that <script>
tag. And it will block parsing the rest of the HTML content. So in this case, the user won't be able to see his fancy div before that JavaScript is downloaded and executed (takes 20 seconds in total).
You can use DOMContentLoaded
to detect whether the initial DOM is loaded and parsed. And your statement in last paragraph is quite correct: every time HTML parser see a <script>
, it will download and execute it synchronously (see Notice 2). After all <script>
are executed and all HTML is parsed, DOMContentLoaded
will be fired.
Notice 1: DOMContentLoaded
will NOT wait for CSS and images
Notice 2: Most browsers have "Speculative parsing" feature. If there are multiple JavaScript files, they will be downloaded simultaneous. However, they will still be executed sequential by the main thread.
For your last question:
Or, does the page paints the page at the same time of parsing DOM tree?
From my own understanding, the answer is YES, the browser will try to paint ASAP. That is to say, the paint engine won't wait for render tree is fully ready. So there should be a separated thread to handle the paint.
Feel free to correct me if any of my understanding is wrong :)
References:
- https://www.chromium.org/developers/the-rendering-critical-path
- http://taligarsiel.com/Projects/howbrowserswork1.htm