Understanding javascript execution order is a crucial aspect of frontend development. In a previous tutorial we introduced the concept of javascript as a blocking resource which executes in reading order of the HTML. This time we will dive a little bit deeper and look into better ways of controlling javascript execution order, rather than physically moving <script> tags further down the page.

Moving script tags is brittle

We previously showed that we could successfully change some properties on an element by executing the related javascript AFTER the element had been rendered.

<html>

<head>
  <title>Javascript load order</title>
</head>

<body>
  <div id="main">
    I am content directly from the HTML
  </div>

  <script>
    const elem = document.getElementById('main');
    elem.style.background = 'blue';
    elem.style.color = 'white';
  </script>
</body>

</html>

It is easy to imagine a scenario where someone else comes along to do some refactoring and wants to move the div somewhere else on the page. It would be far too easy at that stage to overlook the fact that the related javascript would need to be moved too. This means our code is in a very brittle state. It “works for now”, and that’s not a great situation to be in.

Always add script tags at the very bottom of the page (most of the time)

As a rule of thumb, think of javascript execution as the really slow sibling of DOM rendering. Browsers are optimised to do what they do very well. Indeed script execution is actually quite fast, but it is still OUR own code being executed. Let the browser do everything it can to render a page first and then execute “additive” javascript after. This is easily achieved by only ever adding <script> tags to the very bottom of the <body> in the HTML.

There are a few cases where javascript makes sense in the <head>of the page. Indeed most 3rd party code snippets will say their snippet MUST be put in the head. This is not strictly true, and I’m fairly sure some just say that because everyone else does! But it is true that some might not behave 100% effectively if they are added only to the bottom of the page. One example here is the Google analytics code snippet. The reason why is out of scope of this discussion, but know that it is possible that some tracking metrics could be missed when the script is added too low.

That said, for our own standard website or application code, there is never really a good reason to add <script> tags anywhere other than the very bottom of the <body>.

Getting inline javascript to wait for the DOM

Consider 2 scenarios:

  1. You have total control over a page and can move script tags to the bottom
  2. You are working in an existing codebase and cannot safely move script tags to the bottom (because a previous developer hadn’t read this post??)

It’s always a good idea in both cases to wait for the DOM to be rendered. Think of it as getting out of the way and letting the browser’s rendering engine do its thing before you start really executing any javascript. In terms of page experience for the user, this is most likely to have the best chance at creating a good user experience by displaying something meaningful for the user quickly.

Now, the browser has a mechanism to let our code know about all sorts of things that are happening. These are called events. Some examples of events that the browser tells us about are when the user clicks a button or, relevant to our example, that the DOM has finished loading. There is more information about all the browser events at the mdn reference page. In javascript it is quite straightforward to listen for these events. The syntax is as follows:

    (target object).addEventListener(eventName, handlerFunction)

For our example we alter our code to the following:

  <script>
    window.addEventListener('DOMContentLoaded', () => {
      const elem = document.getElementById('main');
      elem.style.background = 'blue';
      elem.style.color = 'white';
    })
    
  </script>

  • Our “target object) is the global “window” object.
  • The event we are listening for is DOMContentLoaded

In doing this we’ve basically just told the browser “Please tell me when the DOM is loaded, and then execute this function for me”.

More technically, the handler only executes after the DOMContentLoaded has fired. Remember our div?

  <body>
  <div id="main">
    I am content directly from the HTML
  </div>
  ...
  </body>
That <div> will now be loaded in the DOM so our call to getElementById will now be able to find it. Our code can then successfully change the colours. Yay!

Thanks for reading.

This is part of a series, gently introducing web performance concepts.

Tags: #frontend