JavaScript JavaScript Basics Hello, JavaScript! Add JavaScript to HTML

Luca Vicini
PLUS
Luca Vicini
Courses Plus Student 8,594 Points

JavaScript files order of execution and rendering

Hello,

There is something that I cannot understand. Here is my html file:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>JavaScript Basics</title>
    <link href="css/style.css" rel="stylesheet">
  </head>
  <body>
    <main>
      <h1>Hello, JavaScript!</h1>
    </main>
    <script src="js/test.js"></script>
  </body>
</html>

In theory the <h1> tag should render before the dialog box (the alert statement within test.js). In practice I get the dialog box before anything else. Why is that happening?

Cheers Luca

3 Answers

Luca Vicini
PLUS
Luca Vicini
Courses Plus Student 8,594 Points

Hi Brandon, What you say makes a lot of sense. But then if I see the course video, at minute 6:5 it says:

One advantage of placing your script near the bottom of the page is that it lets the browser load and display the HTML before running the JavaScript. Notice what happens if I move this script tag to the bottom of the page just before the closing body tag. When I save this file and refresh the page, now the alert that's directly in the HTML loads first before anything else, then the content of the page displays.

I cannot reproduce the behaviour of the course. Am I missing something here?

Brandon White
seal-mask
.a{fill-rule:evenodd;}techdegree seal-36
Brandon White
Treehouse Moderator

Luca,

You make a good point. This is the first time I'm having to think about how the browser renders the page this deeply, and so I thank you for asking this question.

I'm going to shoot this question to other mods to see what they say. But I don't want to leave you with nothing until then, so here are my thoughts...

My understanding from reading several articles is that the page isn't actually rendered/painted by the browser until after all the javascript is executed/parsed. The order appears to be:

  1. Construct the DOM
  2. Construct the CSSOM
  3. Merge the DOM and CSSOM to Construct a Render Tree
  4. Layout of the page, Painting of the page

The html is parsed in order to construct the DOM, and while doing that the browser would run into the script tag, then block the rest of the DOM construction, while it executes the script.

When I reproduce your code, sometimes the browser is showing the h1 while the alert has popped up, and other times the h1 doesn't show until after I close the alert. I don't really know why this happens.

The only guess I can make to this behavior is that although the javascript is executed before the browser renders the page, sometimes the browser is able to render the page before the output of the javascript is made visible (and sometimes it's not).

Adding the external scripts files to just before the closing body tag means that if you're manipulating a DOM element in your script, the DOM element will have already been constructed (and so your script shouldn't throw an error or otherwise not be able to execute because of a DOM element that hasn't been constructed yet), but based on my little bit of research I'm not sure that placing your script near the bottom of the page actually means that the browser will load and display the html before running the JavaScript.

That said, Guil has a lot more experience than I do. Lol.

I'll ask around and see if I can provide you with any more information.

Here's a link to another article I've read to help me compose this response:

https://medium.com/jspoint/how-the-browser-renders-a-web-page-dom-cssom-and-rendering-df10531c9969

Brandon White
seal-mask
MOD
.a{fill-rule:evenodd;}techdegree seal-36
Brandon White
Treehouse Moderator

Hi Luca,

The browser can’t display the web page until it requests, receives, and interprets the javascript file (it needs to make sure no code in the script affects how the contents of the page should be displayed).

And so, the alert is executed before the browser ‘paints’ the page.

https://varvy.com/pagespeed/critical-render-path.html

If you want the page to load first before test.js is executed you could place all of your code in your test.js file within the callback of the DOMContentLoaded event.

https://developer.mozilla.org/en-US/docs/Web/API/Document/DOMContentLoaded_event

You might could also add the async attribute to the script tag in your html file.

Luca Vicini
PLUS
Luca Vicini
Courses Plus Student 8,594 Points

Hello Brandon, Thanks for your answer. I wen't through the articles. In your previous message you said:

When I reproduce your code, sometimes the browser is showing the h1 while the alert has popped up, and other times the h1 doesn't show until after I close the alert. I don't really know why this happens.

If the execution is not deterministic, there must be a race condition between the rendering of the html page and the execution of the JS code. This is just a thought

If I change test.js code to:

setTimeout(function(){ alert("this is running after the h1 with delay"); }, 0);

Now the alert box is appearing after the h1 has been rendered, regardless if I place the call to test.js in the <head> section or just before the closing </body> tag. Note that the timeout in the function is zero millisecond

I am a bit confused

Brandon White
seal-mask
.a{fill-rule:evenodd;}techdegree seal-36
Brandon White
Treehouse Moderator

Because setTimeout is asynchronous, the JavaScript engine passes it to the browser, where it gets passed to the queue. And when the event loop comes back around, then the code is executed.

Now if the time was a specific amount a ms and not 0, then the browser would take a little more time before passing the alert function back to the queue. But the reason the alert always executes after the h1 is rendered in that specific case is because regardless of the amount of milliseconds set to pass before alert is called, it always has to be passed, by the JS engine, first to the browser then to the queue.

Take the following:

app.js
setTimeout(() => {
    console.log(I was called first)
}, 0)
console.log(I was called second)

“I was called second” should always be logged to the console first.

The order of this would be:

  1. Add setTimeout to call stack
  2. Pass setTimeout to browser to be handled
  3. Add second console.log function to call stack
  4. Execute second console.log function
  5. While 3 and 4 are happening the browser passes the first console.log function to the queue
  6. Once their are no more functions in the call stack to execute, the JS engine checks the queue to see if any functions are waiting
  7. The JS engine adds the first console.log function to the call stack
  8. The first console.log function is executed

So even though the amount of time I’m telling JS to wait before logging the first statement to the console is zero milliseconds, it still happens after the second console.log function because it can’t be executed until after it’s pulled from the queue and added back to the stack.

You may have a point about the race conditions though.