Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When does parsing HTML DOM tree happen?

I always see a rendering flow for a web page like the following image shows: enter image description here

So the painting only begins after DOM tree is parsed and CSSOM is created, right? Another saying is, putting <script> in the end of <body> is the best practice so that the page renders something before script is downloaded.

My question is, when does parsing DOM tree happens and how can we say it is done? In my understanding, <script> in the end is also part of DOM tree, and only if the script loaded can we call the DOM tree is created. The browser reads the html file from top to bottom, creating the DOM Tree and when it sees a <script>, it stops to download and execute it until the parse goes through the whole page. Or, does the page paints the page at the same time of parsing DOM tree?

like image 927
JasmineOT Avatar asked Dec 14 '15 14:12

JasmineOT


People also ask

How does parsing work in HTML?

HTML parsing involves tokenization and tree construction. HTML tokens include start and end tags, as well as attribute names and values. If the document is well-formed, parsing it is straightforward and faster. The parser parses tokenized input into the document, building up the document tree.

What is DOM tree parsing?

The DOMParser interface provides the ability to parse XML or HTML source code from a string into a DOM Document . You can perform the opposite operation—converting a DOM tree into XML or HTML source—using the XMLSerializer interface.

How DOM parsing is done by browser?

Once the browser receives the first chunk of data, it can begin parsing the information received. Parsing is the step the browser takes to turn the data it receives over the network into the DOM and CSSOM, which is used by the renderer to paint a page to the screen.

How DOM tree is implemented?

The nodes of every document are organized in a tree structure, called the DOM tree, with the topmost node named as "Document object". When an HTML page is rendered in browsers, the browser downloads the HTML into local memory and automatically parses it to display the page on screen.


3 Answers

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.

Network tab

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.

Timeline tab

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:

  1. The resource must be fetched from the network (this might be a slow process due to DNS lookups and speed of connection).
  2. The content of the resource is passed to the Javascript interpreter.
  3. 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)
like image 84
pesla Avatar answered Sep 28 '22 20:09

pesla


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:

  1. https://www.chromium.org/developers/the-rendering-critical-path
  2. http://taligarsiel.com/Projects/howbrowserswork1.htm
like image 37
Calvin Zhang Avatar answered Sep 28 '22 20:09

Calvin Zhang


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.

like image 33
Alex Deas Avatar answered Sep 28 '22 18:09

Alex Deas