Author: Ben Bozzay
Optimization requires us to find the right balance of tasks that pause the DOM and tasks that can be deferred until after it loads.
As we've discussed, the HTML parser evaluates nodes from the top down and from parent to child.
<html> <head> <title>This title appears in the browser's tab</title> <script src="main.js"></script> <link rel="stylesheet" href="style.css"> </head> <body> <h1>Heading that gets rendered</h1> </body> </html>
<head></head> tags contain nodes that are not rendered (title, meta, scripts, links, etc.), while nodes within the
<body> are visually painted (h1, p, div, img, etc.) for the user to view and interact with.
When the DOM parser encounters a standard
<script src> or
<link rel="stylesheet" href> tag, the resource is downloaded and parsed before DOM construction continues. Inline scripts and styles pause construction while they are parsed.
When referenced in the
<head></head> tags, the DOM parser is delayed from discovering nodes that actually get rendered to the user, such as HTML between the
It's a common practice to reference all scripts and styles within the head tags of the HTML. However, this is frequently not necessary and can significantly delay the HTML parser.
When only loading scripts and styles in the
<head>, the page renders content all at once.
<html> <head> <script src="/dist/js/debug/delay.js"></script> <script> delay(3000) </script> </head> <body> ...
<head> node will pause DOM construction by at least 3 seconds, delaying discovery of the
The screen changes from blank to fully rendered.
Let's move our delay JS to just before the closing
... <script src="/dist/js/debug/delay.js"></script> <script> delay(3000) </script> </body>
Notice that instead of a blank screen, we quickly see some elements on our page. Placing our script towards the end of the body means that we probably aren't delaying the DOMParser from finding and rendering any important nodes.
By differing when the DOM parser encounters scripts or styles, we can benefit from incremental rendering.
Let's demonstrate this incremental building and rendering process by updating our
<main> section to include the delay script between
<body> <main> <h2>Before Delay 3000</h2> <script src="/dist/js/debug/delay.js"></script> <script> delay(3000) </script> <h2>After Delay 3000</h2> <h2>Before Delay 3200</h2> <script> delay(3200) </script> <h2>After Delay 3200</h2> <h2>Before Delay 3100</h2> <script> delay(3100) </script> <h2>After Delay 3100</h2> <h2>End Delay</h2> </main> </body>
And if we examine our page, these nodes have a noticeable delay before being rendered. For non-critical or progressively enhanced content, we can utilize incremental building to reduce DOM parser delays of critical, above-the-fold content.
So we've established that large and inefficient JS or CSS references in the
<head> of a page can delay the DOM parser from discovering nodes that get "painted" on the screen. This is a feature, not an undesirable behavior, because resources critical for rendering the initial view when the page is loaded (above-the-fold) should be downloaded and parsed before any nodes get painted.
Deciding whether to place either type of resource reference in the
<head> or within the
<body> depends on what is critical for displaying the initial view to the user.
As a general rule, JS/CSS resources critical for rendering above-fold content should be included within the
<head></head> HTML tags and prioritized.
These scripts and styles should already be downloaded and parsed before visually-rendered nodes get painted to the screen. We can guarantee this by referencing these resources in the
<head></head> of the site.
Conversely, by differing some JS and CSS we can reduce the time it takes for the DOM Parser to discover nodes that get visually rendered.
jQuery.ready()events to call a function after the DOM loads, we can just place these scripts directly after the required HTML
CSS for the above-the-fold content should always be included in the head. Otherwise, the HTML will render before styles are calculated. This causes a flash of broken content before the styles are calculated.