0

Hi guys I’m new to JavaScript and web development. I came across this question recently about the location of the script tag. I know it’s a common question and I’ve viewed some answers on stackoverflow also this style guide on google. but I am still not very clear on this problems.

For example, I have I html page with an external script js file like this

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title></title>
    <script src='js.js'>
    </script>
  </head>
  <body>
  </body>
</html>

and the js file is

var loadTime = document.createElement('div');
loadTime.textContent = 'You loaded this page on: ' + new Date();
loadTime.style.color = 'blue';
document.body.appendChild(loadTime);

It seems to me that this js file is not dependent upon any DOM elements of the html file being available so it should not matter where I put this script tag. But it turns out I have to put this script tag to the bottom of the body closing tag, otherwise the date won't appear on the page as expected. Another workaround is using defer attribute in the script tag like this

<script src='js.js' defer></script>

This is what baffles me, if a script has any operation related to DOM, it seems to me that it cannot be put at inside the head tag at the front without a defer or async attribute in it. Why this google style guide https://developers.google.com/speed/docs/insights/BlockingJS still suggest we can write inline script in the head tag given accessing and operating on DOM are incredibly common in any script file.

According to http://caniuse.com/#feat=script-defer, 94.59% of all browsers support this. 94.92% support it at least partially. Why the async and defer attributes are not used widely? I mean, I viewed a lot of HTML sources out there, and I just don't see the async and defer attributes anywhere?

Joji
  • 2,372
  • 1
  • 12
  • 31
  • Because I generally use jQuery, the equivalent to forqzy's answer is to wrap your javascript in the `ready` function: `$(document).ready(function() { ... })` – Stephen P Jul 12 '18 at 23:13
  • 1
    _"It seems to me that this js file is not dependent upon any DOM elements of the html file being available"_ — The last line in the script is very much dependent on the `document.body` element, which is created after the head element where the script is running. – Lennholm Jul 12 '18 at 23:48
  • To add to @Lennholm's comment, the document loads top down line by line which is why the body hasn't been created yet – James Jul 13 '18 at 00:10

2 Answers2

2

So here are some explenations.

  1. Using <script></script> in HTML without any extra attributes will block HTML parsing (in other words 'loading html to browser window') from that point. Specified script will be then fetched and executed upon successful download. After that, execution will be resumed (page will be loaded).
  2. Using <script async></script> allow HTML parser not to block parsing until the script is downloaded.
  3. Using <script defer></script> allow HTML to be fully parsed, and script code will be executed after that.

So to anwser your question from the topic - scripts in <head></head> can be included (and will work properly) if they do not require to acces things that are not there yet. In your example you are trying to append sth to body (which is not yet there). If you are using <script async></script> in <head></head> it is also not guaranteed to work. Eg. script is tiny (almost instant download) - it will be executed before html is fully parsed (results in accessing things that are not there yet). We can time async requests, it's part of their beauty.

Using async makes sense if fetched script is not accesing DOM straightforward.

Using defer makes sense eg. if JS file is big (eg. fetch takes 5sec) and we want to show user sth. on the page. After script is loaded we change the page to it's inteded 'look' via js in script.

Note that these are not all use cases of async and defer.

Moonjsit
  • 494
  • 2
  • 10
  • Thank you for your answer! So basically using could result in race condition. In my case, where I was accessing body DOM element, I should've used defer attr in script tags. Right? Given that, is the trick that @forqzy introduced below still relevant since we can use defer attr instead? – Joji Jul 13 '18 at 05:10
  • It depends on how your app is deisgned and what technologies you are using. If the site is fully generated by eg. React then you would like html to be shown to user after script is loaded and executed, not to show blank site and after that execute js to populte it's contents. – Moonjsit Jul 13 '18 at 15:18
0

Suggest you check the answer of this load and execute order of scripts

The normal way is in the head tag only loading the javascript. Then after the whole html file is loaded, in the document onload call your functions.

document.addEventListener('DOMContentLoaded', function() {
  console.log('document - loaded - ');

  //call your functions
}, true);
forqzy
  • 391
  • 1
  • 9
  • 2
    More precisely — when the HTML itself is loaded and parsed to create the DOM the `DOMContentLoaded` event is fired; it doesn't have to wait for _everything_ (such as images) to finish loading. At that point it's safe to search and manipulate the DOM. – Stephen P Jul 12 '18 at 23:11
  • thank you for your answer! In this case, the browser will download the external script file and while blocking the HTML when it encounters the script tag in the head tag right? My question is, it seems more reasonable to put the script tag just before the closing body tag. in that case we know the script tag will be executed only when HTML itself is fully loaded and it won't block the rendering of the HTML. – Joji Jul 12 '18 at 23:25
  • While this is a strategy to wait until the DOM is loaded, this doesn't actually address the question regarding scripts in the document head. – James Jul 13 '18 at 00:05