Why You Need Script defer and async Attribute

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.

References