28

I know this question was asked many times, but I haven't found answer. So why its recommended to include scripts at the end of body tag for better rendering?

From Udacity course https://www.udacity.com/course/ud884 - rendering starts after DOM and CSSOM are ready. JS is HTML parse blocking and any script starts after CSSOM is ready.

So if we got:

<html>
    <head>
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <title>CRP</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <!-- content -->
        <script src="script.js"></script>
    </body>
</html>

CRP would be:

CSSOM ready > JS execute > DOM ready > Rendering

And if script is at head:

<html>
    <head>
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <title>CRP</title>
        <link rel="stylesheet" href="styles.css">
        <script src="script.js"></script>
    </head>
    <body>
        <!-- content -->
    </body>
</html>

CRP would be the same:

CSSOM ready > JS execute > DOM ready > Rendering

This question is only about "sync" scripts (without async/defer attribute).

Artem Svirskyi
  • 7,305
  • 7
  • 31
  • 43
  • http://stackoverflow.com/questions/436411/where-is-the-best-place-to-put-script-tags-in-html-markup – epascarello Jun 04 '15 at 20:17
  • 1
    Rendering is not done at once, but incrementally. If a JS is blocking at the end of the body, the page up to that point will be rendered (if CSSOM is ready). – ernesto Oct 15 '15 at 14:31

7 Answers7

25

Scripts, historically, blocked additional resources from being downloaded more quickly. By placing them at the bottom, your style, content, and media could download more quickly giving the perception of improved performance.

Further reading: The async and defer attributes.

YakovL
  • 7,557
  • 12
  • 62
  • 102
Sampson
  • 265,109
  • 74
  • 539
  • 565
  • 8
    I noticed you said historically. Is that still true, or do newer browsers work around this? – Chris Walter Jun 04 '15 at 20:04
  • @ChrisWalter The number of connections to the server in the past were more limited, and with the dawn of HTTP2 we can get all of the data down much faster. Additionally, scripts can have their execution deferred these days as well. – Sampson Jun 04 '15 at 20:05
  • So you are referring to adding defer to the end of your script tag as seen here: http://www.w3schools.com/tags/att_script_defer.asp – Chris Walter Jun 04 '15 at 20:08
  • 1
    @ChrisWalter You should look into both the [`async`](http://www.w3.org/TR/html5/scripting-1.html#attr-script-async) and [`defer`](http://www.w3.org/TR/html5/scripting-1.html#attr-script-defer) attributes, and *preferably* not via w3schools. – Sampson Jun 04 '15 at 20:12
  • W3Schools was my first hit on Google and seemed to get the point across :) I'd add to your answer that those attributes are not supported until Internet Explorer 10, so it'd still be worth adding your JavaScript to the end of your HTML. – Chris Walter Jun 04 '15 at 20:17
  • @ChrisWalter [Internet Explorer 4 supported `defer`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#Browser_compatibility). Its implementation was revised in version 10. – Sampson Jun 04 '15 at 20:18
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/79695/discussion-between-chris-walter-and-jonathan-sampson). – Chris Walter Jun 04 '15 at 20:21
  • I stand corrected. Easy to misinterpret things on those available in version sites. – Chris Walter Jun 04 '15 at 20:24
  • The links given in the answer is not working. Please update. @Sampson – jay May 15 '19 at 03:32
5

In my opinion, this is an outdated practice. More recently, the preference is for JavaScript to separate any code that requires the DOM to be present into a "DOMContentLoaded" event listener. This isn't necessarily all logic; lots of code can initialize without access to the complete DOM.

It's true that this causes a small moment when only the script file is being retrieved, and nothing else (for instance, images). This small window can be skipped by adding the async attribute, but even without it I recommend putting script tags in the head so that the browser knows as soon as possible to load them, rather than saving them (and any future JS-initiated requests) for last.

Katana314
  • 8,429
  • 2
  • 28
  • 36
5

It is a best practice to put JavaScript tags just before the closing tag rather than in the section of your HTML.

The reason for this is that HTML loads from top to bottom. The head loads first, then the body, and then everything inside the body. If we put our JavaScript links in the head section, the entire JavaScript file will load before loading any of the HTML, which could cause a few problems.

1.If you have code in your JavaScript that alters HTML as soon as the JavaScript file loads, there won't actually be any HTML elements available for it to affect yet, so it will seem as though the JavaScript code isn't working, and you may get errors. 2.If you have a lot of JavaScript, it can visibly slow the loading of your page because it loads all of the JavaScript before it loads any of the HTML. When you place your JavaScript links at the bottom of your HTML body, it gives the HTML time to load before any of the JavaScript loads, which can prevent errors, and speed up website response time.

One more thing: While it is best to include your Javascript at the end of your HTML , putting your Javascript in the of your HTML doesn't ALWAYS cause errors. When using jQuery, it is common to put all of your code inside a "document ready" function:

$("document").ready(function(){ // your code here });

This function basically says, don't run any of the code inside until the document is ready, or fully loaded. This will prevent any errors, but it can still slow down the loading time of your HTML, which is why it is still best to include the script after all of the HTML.

adi1ya
  • 404
  • 1
  • 7
  • 10
2

Images placed below the script tag will wait to load until the JS script loads. By placing the script tag at the bottom you load images first, giving the appearance of a faster page load.

bcr
  • 3,791
  • 18
  • 28
  • Yes, but it depends on the number of images. Since browsers handle `src` simultaneously till some number of requests. Also note that you can always use `async` property for the ` – Roko C. Buljan Jun 04 '15 at 20:06
  • The [HTTP/1.1 specification](http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.1.4) suggests that browsers download no more than two components in parallel per hostname. @RokoC.Buljan – bcr Jun 04 '15 at 20:10
1

I think it depends on your website or app. Some web apps are based on JavaScript. Then it does not make sense to include it at the bottom of the page, but load it immediately. If JavaScript just adds some not so important features to some content based page, then better load it at the end. Loading time will almost be the same, but the user will see the important parts earlier (before the page finished loading).

It’s not about a whole site loading faster, but giving a user the impression of some website loading faster.

For example: This is why Ajax based websites can give a much faster impression. The interface is always the same. Just some content parts will alter.

1

This was an extremely useful link. For any given webpage, a document object model is created from the .html. A CSS object model is also created from .css.

We also know that JS files also modify objects. When the browser encounters a tag, the creation of DOM and CSS object models are immediately halted when the script is run because it can edit everything. As a result, if the js file needed to extract information from either of the trees (DOM and CSS object model), it would not have enough information.

Therefore, script srces are generally at the end of the body where most of the trees have already been rendered.

DFeng
  • 347
  • 3
  • 5
0

Not sure if this helps, But from this resource script-tag-in-web the inline script is always render blocking even if kept at the end of body tag.

Below inline script is first render blocking.Browser will not paint anything on screen till the long for loop is executed

    <!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
    <title>Critical Path: Script</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script>
let word = 0
        for(let i =0; i<3045320332; i++){
            word += i;
        }
      var span = document.getElementsByTagName('span')[0];
      span.textContent = 'interactive'; // change DOM text content
      span.style.display = 'inline'; // change CSSOM property
      // create a new element, style it, and append it to the DOM
      var loadTime = document.createElement('div');
      loadTime.textContent = word + 'You loaded this page on: ' + new Date();
      loadTime.style.color = 'blue';
      document.body.appendChild(loadTime);
    </script>
  </body>
</html>

But'index.js' below is not initial render blocking, the screen will be painted , then once external 'index.js' is finished running the span tag will be updated.

    <!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link href="style.css" rel="stylesheet" />
    <title>Critical Path: Script</title>
  </head>
  <body>
    <p>Hello <span>web performance</span> students!</p>
    <div><img src="awesome-photo.jpg" /></div>
    <script type="text/javascript" src="./index.js">
    </script>
  </body>
</html>

index.js

let word = 0
    for(let i =0; i<3045320332; i++){
        word += i;
    }
  var span = document.getElementsByTagName('span')[0];
  span.textContent = 'interactive'; // change DOM text content
  span.style.display = 'inline'; // change CSSOM property
  // create a new element, style it, and append it to the DOM
  var loadTime = document.createElement('div');
  loadTime.textContent = word + 'You loaded this page on: ' + new Date();
  loadTime.style.color = 'blue';
  document.body.appendChild(loadTime);
Ayyappa Gollu
  • 906
  • 7
  • 16