The Importance of Script Tag Placement
Traditionally, script tags were positioned within the <head>
section of an HTML document. However, this placement was not without its drawbacks. The browser would halt the parsing of the HTML document to fetch and execute the script. This pause could lead to a noticeable delay in the loading of the webpage, negatively impacting the user experience.
To counteract this issue, developers started placing scripts at the bottom of the page, just before the closing </body>
tag. This allowed the HTML document to be fully parsed and the webpage to load before the script was executed. Although this mitigated the issue of page loading delays, it was not a foolproof solution. The execution of scripts could still potentially block the rendering of the webpage, and the order of script execution was not guaranteed.
Enter async and defer
To address these challenges, the async
and defer
attributes were introduced. These boolean attributes, which can be added to the script tag, allow scripts to be fetched asynchronously, further enhancing the loading performance of webpages.
The async
attribute allows the script to be fetched in the background while the HTML document continues to be parsed. However, once the script is ready, the HTML parsing is paused, and the script is executed. This means that the execution order of multiple scripts with the async
attribute is not guaranteed — they will be executed in the order they are fetched and ready, which might not be the same as the order in the HTML document.
Here’s an example of how the async
attribute is used:
<script async src="example-script.js"></script>
On the other hand, the defer
attribute also allows the script to be fetched in the background, but unlike async
, the script execution is deferred until the HTML document has been fully parsed. This ensures that scripts are executed in the order they appear in the HTML document.
Here’s an example of how the defer
attribute is used:
<script defer src="example-script.js"></script>
async vs defer: A Performance Comparison
Without using async
or defer
, placing a script in the <head>
of the document pauses the parsing of the HTML document until the script is fetched and executed. This can lead to longer page loading times, especially for larger scripts or slower network connections.
If the script is placed at the end of the <body>
tag without async
or defer
, the HTML document parsing finishes before the script is fetched and executed. This can improve page loading time but might delay the script execution, which could be a problem if the script is critical for the webpage.
When using the async
attribute in the head, the script is fetched asynchronously, and the HTML parsing is only paused when the script is ready to be executed. This can lead to faster page loading times, but the execution order of scripts is not guaranteed.
When the defer
attribute is used in the head, the script is fetched asynchronously, and the execution is deferred until the HTML parsing is complete. This guarantees the execution order of scripts and allows the browser to continue parsing the HTML document while the script is being fetched, leading to potentially faster page loading times.
Blocking Parsing and Rendering
It’s important to note that the async
attribute blocks HTML parsing while the script is being executed. In contrast, the defer
attribute does not block HTML parsing, leading to potentially faster page loading times. Neither async
nor defer
make guarantees about blocking rendering — this largely depends on the implementation of the script itself.
domInteractive and Execution Order
Scripts with the defer
attribute are executed after the domInteractive
event. This event is triggered after the HTML document is loaded and parsed, and the Document Object Model (DOM) is built. In contrast, scripts with the async
attribute are executed as soon as they are ready, potentially before the domInteractive
event. This means that scripts with the async
attribute can be executed out of order, while scripts with the defer
attribute maintain their relative order as defined in the HTML document.
Best Practices for Using async and defer
To optimize page loading performance, it’s generally recommended to place scripts in the <head>
of the document and add the defer
attribute to the script tag. This allows the script to be fetched asynchronously while the HTML document is being parsed, and the script execution is deferred until after the parsing is complete. This approach triggers the domInteractive
event earlier, improving the perceived page loading speed.
However, there might be cases where the async
attribute is more suitable. If a script is independent, meaning it doesn’t rely on any other scripts, and the order of execution does not matter, the async
attribute might be a better choice. This allows the script to be executed as soon as it’s ready, potentially speeding up the time until the script is run.
Here’s a best practice example of how to use defer
:
<head>
<script defer src="script1.js"></script>
<script defer src="script2.js"></script>
</head>
In this example, both scripts are fetched asynchronously while the HTML document is being parsed. The scripts are executed after the HTML parsing is complete, in the order they appear in the document. This ensures that script1.js
is executed before script2.js
, even if script2.js
finishes fetching first.
By understanding the differences between async
and defer
script tags, developers can make informed decisions about which attribute to use to optimize script loading and execution, enhancing the user experience and improving the performance of their webpages.