First we have to understand these questions:
- When is HTML rendering blocked?
- How can we affect the behavior of loading and executing JS files?
On initial page load, whenever the browser is instructed to load a referenced JS file it stops further processing of HTML until the JS file is loaded and executed. The same goes with CSS files.
<script src="file.js" />
Blocking HTML rendering is bad for two reasons: First, it takes longer until the browser finishes rendering and displaying the content to the user. And second, you’ll get a problem if your JS code tries to access HTML elements that have not already been parsed.
That’s why you should load files at the bottom of your
<body>. But there is more you can do. The
async property allows you to load a JS file asynchronously, meaning that HTML continues to render while the JS file is loading, but HTML parsing still stops for JS execution.
<script src="file.js" async />
async has a catch: You have no control over when your JS is going to be executed which is a problem if your JS code depends on having other JS files loaded first.
Loading JS files using
defer loads JS code in parallel to the HTML parsing (just like
async), but unlike
async it also defers execution until HTML parsing is done:
<script src="file.js" defer />
That means the order of specifying deferred JS files in your HTML matters.
Loading JS asynchronously and/or deferring its execution becomes far more important when using HTTP/2, because of how HTTP/2 loads files in parallel.
To summarize it: Use