In order to give users a great page experience on your website, the goal is as simple as getting a fully loaded and settled page in front of the user as quickly as possible. That is to say we want:
- All text content rendered
- All images rendered
- Stable DOM layout. e.g. No shuffling of content as new images or adverts are added
- Interaction ready. e.g. button handlers ready before the user tries to click
A Simple Page
Normally a web page would be a lot more complex, but the principles stay the same, so let’s keep it clear with simplicity. Let’s start with a super simple HTML page:
Running that (using http-server) in the browser we’ll see:
- Inline script
- External script
Here we’ll keep it simple and deal with inline scripts. A further explanation for external scripts will come in a follow up.
<script> console.log('hello, world'); </script>
<head>tag or anywhere in the
<body>tag. The fact they are embedded directly means:
- There is no extra download to fetch the script
- The initial HTML payload increases in size
<head> tag. Our head tag now looks like this:
That’s great! Our script has logged some output to the console as expected. However we haven’t figured out much about load order here. In order to demonstrate the render blocking nature of the script, let’s tweak our code to update the background colour of the
<div>. The code now looks like this:
As you can see we’re doing some fairly basic DOM manipulation here:
- find the element with id “main”
- change it’s background and foreground colours
- say humblest of apologies to the css overlords for that oversimplified code
However, here’s the output:
Oh no! That didn’t work at all! The colours have not changed, and there is an error in the console. What’s going on here?
To understand the concept of render blocking resources all you have to do is understand that the browser is going to process the HTML as you would read a page of a book.*
*note that there are some subtleties in there (of course there are) but they are not important for this discussion.
Trying to manipulate an element that does not exist
<head> tag is executed BEFORE the
<div id="main"> is rendered (technically, added to the DOM). That means for the code that is trying to get the element by id:
const elem = document.getElementById('main');
null. Given that
elemis null, the next line will obviously fail with the “
cannot read properties of null” exception.
elem.style.background = 'blue';
To recap the important bits in bullet points:
- browser begins to process the html (reading top to bottom like a book)
<head>, trying to
- no such element exists so we have the
elemvariable with a null value
- exception is thrown when trying to read
- browser logs the exception to the console
- browser continues to process the rest of the html AFTER the
- browser adds
<div id="main">to the DOM
We know from the discussion so far that our script threw an exception because it was trying to manipulate a DOM element that did not yet exist. From that we can figure out there is a simple solution - move the script tag BELOW (remember the book analogy) the DOM elements it is trying to manipulate.
And yay! There is no console error and our script has indeed changed the colours of the
- the browser process HTML top to bottom
- when a
Have a play with the code at https://github.com/djarcher/tutorial-inline-script-execution
Thank you for reading.
This is part of a series, gently introducing web performance concepts.