Identifying the Problem
<body> <!-- Web page content --> <script src="script.js"></script> <!-- Web page content --></body>
Problem 1: The browser spends a lot of time downloading and parsing scripts right after entering the webpage: If scripts are added to the webpage, it will cause the rendering of the webpage to take longer to complete because the browser will stop rendering the page while loading the script, and will only continue after loading and executing it. Having the webpage rendering paused halfway is a very poor experience for users.
<p>First element</p>
<script> console.log(document.querySelectorAll('p')); // NodeList [ p ]</script>
<!-- The above script cannot select the subsequent DOM content --><p>Second element</p>
Problem 2: DOM elements rendered after the script cannot be selected: If the script attempts to select DOM elements that have not yet been constructed, it will result in the issue of not being able to select them.
Solutions
Modern browsers support the built-in HTML defer and async attributes, which allow the browser to load scripts without stopping the rendering of the webpage. The main difference between them lies in the timing of execution. Below are three methods introduced: not using any attributes, using the defer
attribute, and using the async
attribute.
Solution 1: Place scripts at the bottom
<body> <h1>Web page content</h1> <script src="script.js"></script></body>
This was a common and straightforward approach in the past, but it causes the script to only start loading after the entire webpage has finished rendering, which can slow down the execution of the script in long HTML documents or slow network speeds.
Solution 2: defer
<head> <script src="script.js" defer></script></head><body> <p>The first element</p> <p>The second element</p></body>
defer
is the concept of downloading something first but executing it later. By doing this, we can fetch script resources at the beginning of the webpage (usually in the <head>
) and execute them only after the DOM has been rendered.
Solution Three: async
<script src="script.js" async></script>
Summary
In most cases, choosing either attribute will improve the webpage’s loading speed. However, if there are dependencies between scripts, special attention is needed.
Is defer after the DOMContentLoaded event?
The DOMContentLoaded
event indicates that the HTML document has been completely loaded and parsed. For defer
scripts, they can create/delete DOM elements, so the DOMContentLoaded
event will only trigger after all defer
scripts have completed execution, ensuring that the final DOM tree structure is ready after all possible DOM updates by defer
scripts.
What happens if both defer and async are added simultaneously?
<script src="script.js" async defer></script>
The scenario of using both attributes simultaneously usually occurs in supporting older browsers that only support the defer
attribute. If async
is not supported, defer
will be used, and the latter described attribute will be treated as a fallback, adopted only when the primary described attribute is not supported.