Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Browser different rendering behavior with different location of script and link tag

I am trying to understand when exactly the HTML is rendered on screen in the browser.

I read this S.O. answer and tried some use cases and I observed something that doesn't fit into the model shared in the link,
also I can't seem to find a consistent mind-model for the observation.

What is the browser rendering order that fits the cases discussed?


Case A: Image-A enter image description here

  1. When HTML parser reaches M1 , M1 is displayed.
  2. Then parser reaches script tag it wait for js file to download and parsed.
  3. Then parser reaches M3, M3 is displayed.

Case A is in-coherence with the order described on most answers. Huray!!! Let's move on.

Case B: Image-B enter image description here

  1. HTML parser reaches M1, M1 is not displayed as in CASE A
  2. HTML parser reaches link tag, it wait for css to download and parser.
  3. M1 and M2 are display, so it waited for stylesheet to download before rendering M1.
  4. Then parser reaches script tag it wait for js file to download and parsed.
  5. Then parser reaches M3, M3 is displayed.

So Case-B shows that it didn't render the M1, it waited for CSS to download before rendering it. So maybe the renderer needs to know the CSS before rendering.

Case:C Image C

enter image description here

So from Case B, we assumed that may be render-er needs to know CSS.
Let's look at Case C:

  1. HTML parser reaches M1, M1 is displayed. It should not have been displayed since as we saw in case B it should have to wait for css to load.
  2. Now parser reaches script, it waits for js to download and parser.
  3. M2, M3 are displayed

Edit: Proposed mind-model which explains the above behavior..(but require review/refinement)

  1. Script is not a renderer blocking tag
  2. link is a renderer blocking tag
  3. Considering that the renderer and HTML parser are two threads.
  4. HTML parser can send stuff to renderer for rendering.
  5. HTML parser can send block signal to renderer..to block renderer to render any html which hasnt been rendered atleast once.
  6. HTML parser can send unblock signal to renderer..to unblock renderer from render any html which hasnt been rendered once.

With above model we can explain CASE B and CASE C:
CASE B Explanation:

  1. HTML parser reaches M1, M1 is send to renderer.
  2. HTML parser reaches Link tag, parser send a blocking signal to renderer.
  3. Renderer before it can render M1 , it got a blocking signal and hence M1 is not display.
  4. HTML parser complete link tag parsing(downloading) and send a unblocking signal to renderer, upon receiving a unblocking signal it renders the M1.
  5. HTML parser reaches M2, M2 is send to renderer.
  6. HTML parser reaches Script tag, since script tag is not a renderer blocking tag, renderer is free to render html.
  7. HTML parser completes parsing of script tag (downloading).
  8. HTML parser reaches M3, M3 is send to renderer.

Similarly we can dry run CASE C, and it fits perfectly in above model.

Is my model correct or something wrong with it ?

like image 381
Bhuvan Avatar asked Oct 29 '15 19:10

Bhuvan


1 Answers

You are almost right. Except that it is simpler - renderer is passive and does not receive "blocking signal". It does not render (update display to reflect dom tree) until someone ask it to.

  1. Your HTML is invalid - you may not put <link rel=...> in body. (Living HTML 4.2.4)

  2. as you figured, stylesheet is a render-blocking resource - it will prevents contents from being rendered, in contrast with script which will pause and render parsed content.

Here is how I'll explain it:

Case B: M1 - stylesheet - M2 - script - M3

  1. Browser gets the HTML. M1 enters DOM tree. M1 is not displayed yet, renderer has not been asked to work.
  2. stylesheet loads. (in effect it prevents M1 from rendering)
  3. stylesheet done. M1 is still not displayed. (two more steps)
  4. M2 enters DOM tree. M1 and M2 is not displayed yet.
  5. script triggers a render, thus displaying M1 and M2, and then loads.
  6. script done. Nothing change (as fas as this case is concerned).
  7. M3 enters DOM tree, not yet displayed.
  8. Document is done, page is rendered, thus M3 is displyed.

These behaviours are intentional. Once upon a time, js was slow and scripts took long time to run, whereas stylesheets was (and still is) important for hiding elements by default. Time may have changed, but these behaviours are unlikely to change.

Case C: M1 - script - M2 - link - M3

  1. Browser gets the HTML. M1 enters DOM tree. M1 is not displayed yet.
  2. script triggers a render, thus displaying M1, and then loads.
  3. script done. Nothing change.
  4. M2 enters DOM tree. It is not displayed yet.
  5. stylesheet loads, in effect blocking M2.
  6. stylesheet done. M2 is still not displayed.
  7. M3 enters DOM tree, not yet displayed.
  8. Document is done, page is rendered, M2 and M3 is displyed.

The actual display process, in term of threads, is very complicated, and in some sense greatly varies by browser. For example, DOM is not thread safe. Think about what that means.

To keep it simple, you can imagine browser processing as data and event flowing between the various modules. Here are some Firefox rendering subsystems:

Diagram showing 10 interlinked modules related to HTML rendering

like image 126
Sheepy Avatar answered Sep 28 '22 10:09

Sheepy