JS Script & Lifecycle
To make an HTML page interactive, we must introduce JavaScript. The entry point for this interaction is the <script> tag, but where and how you place it drastically affects the performance of your website.
Parser Blocking
When a browser loads an HTML file, it reads it top to bottom. If it encounters a normal <script src="..."> in the <head>, it immediately stops parsing the HTML, downloads the JavaScript file, executes it, and only then continues parsing the rest of the HTML. This is terrible for performance because the user stares at a blank screen.
Async vs Defer
To fix parser blocking, HTML5 introduced two attributes:
- async: The script downloads in the background while HTML parses. As soon as it finishes downloading, it stops the HTML parser to execute. There is no guarantee of execution order. Use this for independent scripts (like Google Analytics).
- defer: The script downloads in the background, but it waits for the entire HTML document to be parsed before it executes. Furthermore, deferred scripts execute in the exact order they appear in the document. This is the modern standard for including your application's logic.
DOMContentLoaded
JavaScript often needs to manipulate HTML elements. If the JS runs before the HTML element is parsed, it will throw an error (e.g., `document.getElementById` returns `null`). By using defer, you guarantee that the DOM is fully constructed before your script runs, making it safe to select and modify elements.
View Full Execution Sequence+
1. Browser begins parsing HTML.
2. Browser encounters <script src="app.js" defer> in the <head>.
3. Browser initiates download of app.js in the background.
4. Browser continues parsing HTML without interruption.
5. HTML parsing finishes. DOM is ready.
6. Deferred scripts are executed in order.
7. The DOMContentLoaded event is fired.
