24
JavaScript loading techniques & Performance
Adding external script files to your HTML document is simple that you could do it in your sleep.
But this is not as trivial as you think. Where and how you add your script file majorly influences the performance of your website.
In this post, we will go through the techniques to include external script files to your HTML and look at how this can affect the performance.
We will compare which technique is preferable and efficient over others in varying situations.
This blog post assumes you are familiar with basic HTML, CSS and JavaScript syntax.
We will also learn about the attributes: async
and defer
.
-
As you might already know, external JavaScript files can be included in the:
- head
- body
Before we continue and discuss about these techniques in depth, let's understand what happens when a browser loads a webpage.
- The browser fetches the requested HTML file, and it is parsed.
- The parsed HTML contains references to external scripts and stylesheets.
- These external references are fetched, and are parsed/ loaded.
- Once loaded, the styles from the sheet are applied to the DOM elements, and
- Then the loaded scripts are executed and applied to the page, and the user views the completed visual structure.
- Essentially, this should be the order in which fetching, parsing, loading and execution happens.
- JavaScript files are meant to be applied finally once DOM is complete. But this might vary depending on where you add the script file.
Now enough with all this! Let's get to the actual post!!
- This is the most preferred technique since this strategy ensures that the HTML parses before the script file.
- This order becomes necessary when your script manipulates the DOM element.
<!DOCTYPE html>
<html>
<head>
<title>JavaScript reference inside body</title>
</head>
<body>
<!-- DOCUMENT CONTENT -->
<script src="./src/main.js"></script>
</body>
</html>
- Since I started learning JavaScript, I have always added the
<script>
within the HTML body. - But I did not know, until recently, that this is an old-fashioned way and surprisingly not the recommended way anymore.
- Adding the script reference in the body may give time for the DOM content to load, but a major problem is that the JavaScript loading is blocked.
- When you have multiple (and huge!) scripts in your website it might turn into a nightmare since users will have to wait for scripts to be loaded AND then executed.
- Not only does this degrade the performance of the website, it also frustrates users.
- Because users hate waiting for websites to load!
How do we manage to load JavaScript files, and at the same time retain user experience and optimize website performance?
The simple answer to this is: Add script references inside the head
- Yes, you read it right. Add script references within the
<head>
.
<!DOCTYPE html>
<html>
<head>
<title>JavaScript reference inside body</title>
<!-- Add script file source here -->
<script src="./src/main.js"></script>
</head>
<body>
<!-- DOCUMENT CONTENT -->
</body>
</html>
- But then it's not that simple. Yet another problem is that when you add the script files to your
<head>
, the script files are fetched before the HTML DOM is parsed and loaded completely. - Below shown image depicts an example webpage that displays a message in
<p>
when user clicks the button. Look what happens when you add the script source in the
<head>
.
You get an error "cannot read property addEventListener of null". This happens because the DOM is loaded after JavaScript is fetched, and hence there is no reference to the button.
- But this could be avoided as well. How? By doing this:
document.addEventListener('DOMContentLoaded', function() {
btn.addEventListener('click', () => {
p.textContent = "You clicked me!";
});
});
- The above code adds an event listener to the body which listens for DOM content to be loaded.
- Once the contents are loaded, all the code within the handler function gets executed thud ensuring that JavaScript is executed only after the DOM is loaded completely.
And now if user clicks the buton, there is no error:
This is yet again an old technique.HTML5 provides two new, modern features that prevents blocking of HTML parse and JavaScript load.
The two attributes:
async
and (or)defer
are added to the script tag when it is included in the<head>
.Both the attributes ask the browser to load the script file in a separate thread without blocking the HTML file from being parsed.
<!DOCTYPE html>
<html>
<head>
<title>JavaScript reference inside body</title>
<!-- Add script file source here -->
<script src="./src/main.js" async></script>
</head>
<body>
<!-- DOCUMENT CONTENT -->
</body>
</html>
- This attribute ensures that the script file is loaded without affecting the HTML from being parsed.
- That is, the browser loads/ fetches the script file simultaneously while the HTML is being parsed.
- The HTML parse is not paused, and hence loading of script file does not block the DOM from loading.
- But once the script file is loaded completely, the HTML parse is paused and the script is immediately executed, now blocking the DOM from loading.
- When your webpage has multiple scripts, there is no guarantee that the order in which scripts are fetched, loaded and executed is the same order in which the scripts appear in HTML file.
- Thus use this attribute when:
- Script fetching, loading and execution are independent of each other. That is code in one script does not affect code in another.
- When you need scripts to perform initialization tasks that are required before the actual execution begins.
- When you have scripts that do not manipulate the DOM.
<head>
<!-- Add script file source here -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous" async></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js" integrity="sha384-cVKIPhGWiC2Al4u+LWgxfKTRIcfu0JTxR+EQDz/bgldoEyl4H0zUF0QKbrJ0EcQF" crossorigin="anonymous" async></script>
<script src="./src/init.js" async></script>
</head>
- For example: in the above code, there is no guarantee that jQuery will be fetched and executed first, then Bootstrap and then the init script.
- The order could be say: Bootstrap is executed first, then init and finally jQuery script.
<!DOCTYPE html>
<html>
<head>
<title>JavaScript reference inside body</title>
<!-- Add script file source here -->
<script src="./src/main.js" defer></script>
</head>
<body>
<!-- DOCUMENT CONTENT -->
</body>
</html>
- defer, as the name suggests, loads the script file in a separate thread, but defers the execution of the script file.
- Unlike
async
, script is not executed immediately once the file is loaded, and the DOM load is not blocked. - This attribute ensures the script is executed only when the DOM is completely loaded.
- The order in which the scripts are fetched, loaded, and executed, is the same order in which they appear in the
<head>
.
- Thus use this attribute when:
- The script files in your web page are dependent on each other, and the execution of one script affects the other.
- When your script manipulates the DOM content.
<head>
<!-- Add script file source here -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous" defer></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.min.js" integrity="sha384-cVKIPhGWiC2Al4u+LWgxfKTRIcfu0JTxR+EQDz/bgldoEyl4H0zUF0QKbrJ0EcQF" crossorigin="anonymous" defer></script>
<script src="./src/main.js" defer></script>
</head>
- The execution of scripts in the above code is in the following order: jQuery script, Bootstrap, and finally the main script file.
- As a thumb rule, I would suggest to add script sources within the
<body>
only if the script your website uses is minimal. If you have multiple scripts that are heavy, refer to it within the
<head>
as sourcing within the<body>
blocks JavaScript from loading, thereby affecting the performance of your website.Use async in case the scripts in your website are independent of each other, and you want to execute code before the main JavaScript loads.
Use defer when you have scripts that rely on parsing of HTML and manipulation of DOM elements.
- Here's a visual representation of HTML parsing, and JavaScript loading and execution from the MDN docs.
Thank you so much for your support and reading this blog post.
Help me out by sharing this to your friends, and comment down what you felt about this post.
Do heart, save, unicorn, or do all of it if you enjoyed and learned rom this post!
24