0
votes

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?

2
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
"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
To add to @Lennholm's comment, the document loads top down line by line which is why the body hasn't been created yetJames

2 Answers

2
votes

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.

0
votes

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);