It is necessary to care about the order of loading and execution of scripts inside a HTML file. In this article you will get to know whether a JavaScript file’s loading is blocking or not, its execution is before or after parsing any subsequent HTML content, how to load JavaScript asynchronously, how to defer its execution until the page has finished parsing, etc.
Basic script element
When below script element appear in a HTML file:
<script src="alert1.js"></script>
or
<script>
alert('inline-js');
</script>
The browser will load and execute the script immediately, block parsing. That means if the script code operates any HTML element that appears after it, it will fail because the element has not been parsed yet.
That’s why an old suggestion recommends putting the script elements at the bottom of the document. Now it is not a good idea (the script’s loading/parsing is blocked until the HTML has been loaded), with defer
you can load scripts without blocking parsing and defer the execution until the document finishes parsing
Dynamically created script
alert("inline-js2")
in below code is counted as ‘inline’ scripts too and will get compiled and execute immediately.
<script>
const script = document.createElement('script');
script.textContent = 'alert("inline-js")';
document.body.appendChild(script);
</script>
async
in script element
<script async src="alert1.js"></script>
In the example above, the async
attribute indicates that the script will be loaded in parallel to HTML parsing and executed as soon as it is available. The script execution will block the page from rendering. That means its loading will not block the subsequent HTML parsing, and its execution maybe before the HTML paring completes.
If the async
is present on a module script element like below, the script and its dependencies will be fetched in parallel to HTML parsing, and the module script will be executed as soon as it is available.
<script async type="module" src="a.js"></script>
Dynamically created script
alert4.js
in below example is async
because the scripts created from script default to async
. See more at MDN: Async scripts.
<script>
const script = document.createElement('script');
script.src = 'js/alert4.js';
document.body.appendChild(script);
</script>
Note
In the above example, if the script element is inserted using the
innerHTML
andouterHTML
attributes, it does not execute at all.const e = document.createElement('div'); e.innerHTML = ''; document.body.appendChild(e);
document.write()
(Strongly discouraged to use)
document.write()
can be used to write a string text to the document. Its usage is strongly discouraged for it has very idiosyncratic behavior.Note
The execution order of multiple
async
scripts is not guaranteed as the order they appear in the document.
All major browsers have supported async
.
defer
in script element
The defer
attribute has a similar effect when it comes to loading in parallel. But the script will be executed after the document has been parsed, but before firing DOMContentLoaded
event. That means a deferred script will prevent the DOMContentLoaded
event from firing until the script has loaded and finished execution.
A module script is deferred by default, therefore there is no need to use the defer
for it.
Both asnc
and defer
work with external files which means the src
is set to a file in the script element. Otherwise these attributes would have no effect.
Note
The execution order of multiple
defer
scripts is the order in which they appear in the document.
All major browsers have supported async
.
Summary
The loading and execution order of script is summarized as below diagram which is from HTML Standard – the script element.
Below is an example that helps you to observe the loading and execution order. In the example, each alertx.js
has just one line of code: alert('alert-x');
.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript loading and execution order</title>
<script src="js/alert1.js"></script>
<link href="css/main.css" rel="stylesheet">
</head>
<body>
<script defer src="js/alert2.js"></script>
<script async src="js/alert3.js"></script>
<script>
alert('inline-alert-1');
</script>
<script>
const script = document.createElement('script');
script.textContent = 'alert("inline-alert-2")';
document.body.appendChild(script);
</script>
<script>
const script2 = document.createElement('script');
script2.src = 'js/alert4.js';
document.body.appendChild(script2);
</script>
<h1>Hello world!</h1>
<script>
alert('inline-alert-3');
</script>
</body>
</html>