DEV Community

Bharati Subramanian
Bharati Subramanian

Posted on • Updated on

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.

What is this blog post about?

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.


Prerequisites

This blog post assumes you are familiar with basic HTML, CSS and JavaScript syntax.
We will also learn about the attributes: async and defer.


Introduction

  • As you might already know, external JavaScript files can be included in the:

    1. head
    2. body
  • Before we continue and discuss about these techniques in depth, let's understand what happens when a browser loads a webpage.

  1. The browser fetches the requested HTML file, and it is parsed.
  2. The parsed HTML contains references to external scripts and stylesheets.
  3. These external references are fetched, and are parsed/ loaded.
  4. Once loaded, the styles from the sheet are applied to the DOM elements, and
  5. 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!!


Including Script in the body

  • 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>
Enter fullscreen mode Exit fullscreen mode
  • 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.

RDJ Surprised

  • 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! Monkey frustrated with computer

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

Jimmy Fallon

Including Script in 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>
Enter fullscreen mode Exit fullscreen mode
  • 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>.
    image

  • 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!";
       });
  });
Enter fullscreen mode Exit fullscreen mode
  • 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:
    image
    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.

1. async

 <!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>
Enter fullscreen mode Exit fullscreen mode
  • 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:
    1. Script fetching, loading and execution are independent of each other. That is code in one script does not affect code in another.
    2. When you need scripts to perform initialization tasks that are required before the actual execution begins.
    3. 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/bootstrap@5.0.2/dist/js/bootstrap.min.js" integrity="sha384-cVKIPhGWiC2Al4u+LWgxfKTRIcfu0JTxR+EQDz/bgldoEyl4H0zUF0QKbrJ0EcQF" crossorigin="anonymous" async></script>
     <script src="./src/init.js" async></script>
   </head> 
Enter fullscreen mode Exit fullscreen mode
  • 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.

2. defer

 <!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>
Enter fullscreen mode Exit fullscreen mode
  • 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:
    1. The script files in your web page are dependent on each other, and the execution of one script affects the other.
    2. 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/bootstrap@5.0.2/dist/js/bootstrap.min.js" integrity="sha384-cVKIPhGWiC2Al4u+LWgxfKTRIcfu0JTxR+EQDz/bgldoEyl4H0zUF0QKbrJ0EcQF" crossorigin="anonymous" defer></script>
     <script src="./src/main.js" defer></script>
   </head> 
Enter fullscreen mode Exit fullscreen mode
  • The execution of scripts in the above code is in the following order: jQuery script, Bootstrap, and finally the main script file.

Conclusion

  • 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.

image


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!

Top comments (9)

Collapse
 
vanshsh profile image
Vansh Sharma

Thanks bharti.
Now this is the reason people suggest to read docs. You get the actual working of the language. Pros and cons of the function.
Thanks .

Collapse
 
bharati21 profile image
Bharati Subramanian

Yes, true. Docs teaches you the very minute details of a language.

Collapse
 
arthurguillerm profile image
Arthur Guillerm

Thanks. Very helpful.
In your code example , in part "2. defer" you use 'async' keyword , it should be 'defer' ?

Collapse
 
bharati21 profile image
Bharati Subramanian

Thank you. I am glad it helped you!
Yes, it should have been "defer". I've changed it.
Thank you for pointing it out.

Collapse
 
rakiabensassi profile image
Rakia Ben Sassi

Thanks for sharing! Just a note: defer and asynch are 2 features from HTML5 not from JavaScript.

Collapse
 
bharati21 profile image
Bharati Subramanian

Yes, my bad. Thank you so much for pointing that out! I have made the changes.

Collapse
 
kush__vaibhav profile image
Vaibhav

Thanks for sharing this amazing blog
Very helpful
👏

Collapse
 
bharati21 profile image
Bharati Subramanian

Thank you. I am glad it helped.

Collapse
 
shubhamkumar648 profile image
Shubham Kumar

well articulated very helpfull, and Great presentaion with help of pictorial view it easy to understand