When loading a script on an HTML page, it’s important to consider its impact on the loading performance. The position and method of adding scripts to an HTML page can greatly influence the loading time.

Traditionally, scripts are included in the page using the following syntax:

<script src="script.js"></script>

When the HTML parser encounters this line, it fetches the script and executes it. This process introduces delay, which can negatively affect the loading time of the page. If the script takes longer to load than expected, the visitor may see a blank page until the script is loaded and executed.

The position of the script tag also matters. Initially, script tags are often placed in the <head> tag:

<html>
  <head>
    <title>Title</title>
    <script src="script.js"></script>
  </head>
  <body>
    ...
  </body>
</html>

In this scenario, the parser fetches and executes the script before continuing to parse the body. This delay can be significant. To improve loading performance, it is common practice to place the script tag at the bottom of the page, just before the closing </body> tag. This allows the script to load and execute after the rest of the page is parsed and loaded, resulting in a significant improvement.

For older browsers that don’t support the newer HTML features async and defer, this is the best approach. However, for modern browsers, the async and defer attributes can be utilized.

Both async and defer are boolean attributes and can be used as follows:

<script async src="script.js"></script>
<script defer src="script.js"></script>

If both attributes are specified, async takes precedence in modern browsers, while older browsers that support defer but not async will fallback to using defer.

It’s important to note that these attributes are only effective when used in the head portion of the page and are unnecessary if the script is placed at the end of the body tag.

To showcase the performance differences, let’s compare different scenarios:

  1. No defer or async, script in the head
  2. No defer or async, script in the body
  3. async script in the head
  4. defer script in the head

In the first scenario, without defer or async, the parsing is paused until the script is fetched and executed. Only then does the parsing resume.

In the second scenario, without defer or async, the script in the body does not interrupt the parsing. Instead, parsing continues, and the script is fetched and executed after parsing is complete. This allows the page to appear to the user faster compared to the first scenario.

In the third scenario, with async in the head, the script is fetched asynchronously. Once it’s ready, the HTML parsing is paused to execute the script, and then it resumes.

In the fourth scenario, with defer in the head, the script is fetched asynchronously and executed only after the HTML parsing is done. This approach is similar to placing the script at the end of the body tag. However, the script execution finishes earlier because it can be downloaded in parallel with the HTML parsing. This is the fastest approach.

Using async blocks the parsing of the page, while defer does not. Neither async nor defer explicitly guarantee non-blocking rendering. It is up to the developer to ensure proper rendering, such as waiting for the onLoad event.

Scripts marked with defer are executed right after the domInteractive event, which occurs after the HTML is loaded, parsed, and the DOM is built. CSS and images are still being parsed and loaded at this point. Afterward, the browser emits the domComplete event, followed by onLoad. The timing of the domInteractive event is considered a measure of perceived loading speed.

When it comes to ordering, scripts marked with async are executed in casual order as they become available. In contrast, scripts marked with defer are executed in the order in which they are defined in the markup.

To achieve the best loading performance with scripts, it is recommended to place them in the head section of the page and add the defer attribute to the script tag:

<script defer src="script.js"></script>

This approach triggers the faster domInteractive event. Considering the advantages of defer, it is often a better choice than async in various scenarios. Unless delaying the first render of the page is acceptable, ensure that the necessary JavaScript is executed once the page is parsed.