Categories
html javascript script-tag

Where should I put tags in HTML markup?

1780

When embedding JavaScript in an HTML document, where is the proper place to put the <script> tags and included JavaScript? I seem to recall that you are not supposed to place these in the <head> section, but placing at the beginning of the <body> section is bad, too, since the JavaScript will have to be parsed before the page is rendered completely (or something like that). This seems to leave the end of the <body> section as a logical place for <script> tags.

So, where is the right place to put the <script> tags?

(This question references this question, in which it was suggested that JavaScript function calls should be moved from <a> tags to <script> tags. I’m specifically using jQuery, but more general answers are also appropriate.)

4

  • 1

    in case you’re also just looking for a simple solution and you’re using some server side generator like Jekyll, i recommend including the script with it instead. so much simpler!

    – cregox

    Sep 20, 2019 at 5:17


  • 1

    If coming from a search engine looking for this: Many of the answers are not clear exactly where the ‘script tag’ should be at the end. If the ‘script’ tag is after ‘</body>’, HTML validation will result in Error: Stray start tag script (check option “source” and click “check” to see the HTML source). It should be before ‘</body>’. (The result is similar if the ‘script’ tag is at the very end, after the </html> tag.)

    Nov 21, 2021 at 15:22


  • 1

    This is also addressed in Is it wrong to place the <script> tag after the </body> tag?.

    Nov 21, 2021 at 17:54

  • T.L.D.R. put it inside the <head> tag with defer attribute, or even better make your script type='module'. It is 2022 now.

    – Beki

    May 28 at 12:06

2244

Here’s what happens when a browser loads a website with a <script> tag on it:

  1. Fetch the HTML page (e.g. index.html)
  2. Begin parsing the HTML
  3. The parser encounters a <script> tag referencing an external script file.
  4. The browser requests the script file. Meanwhile, the parser blocks and stops parsing the other HTML on your page.
  5. After some time the script is downloaded and subsequently executed.
  6. The parser continues parsing the rest of the HTML document.

Step #4 causes a bad user experience. Your website basically stops loading until you’ve downloaded all scripts. If there’s one thing that users hate it’s waiting for a website to load.

Why does this even happen?

Any script can insert its own HTML via document.write() or other DOM manipulations. This implies that the parser has to wait until the script has been downloaded and executed before it can safely parse the rest of the document. After all, the script could have inserted its own HTML in the document.

However, most JavaScript developers no longer manipulate the DOM while the document is loading. Instead, they wait until the document has been loaded before modifying it. For example:

<!-- index.html -->
<html>
    <head>
        <title>My Page</title>
        <script src="my-script.js"></script>
    </head>
    <body>
        <div id="user-greeting">Welcome back, user</div>
    </body>
</html>

JavaScript:

// my-script.js
document.addEventListener("DOMContentLoaded", function() {
    // this function runs when the DOM is ready, i.e. when the document has been parsed
    document.getElementById("user-greeting").textContent = "Welcome back, Bart";
});

Because your browser does not know my-script.js isn’t going to modify the document until it has been downloaded and executed, the parser stops parsing.

Antiquated recommendation

The old approach to solving this problem was to put <script> tags at the bottom of your <body>, because this ensures the parser isn’t blocked until the very end.

This approach has its own problem: the browser cannot start downloading the scripts until the entire document is parsed. For larger websites with large scripts and stylesheets, being able to download the script as soon as possible is very important for performance. If your website doesn’t load within 2 seconds, people will go to another website.

In an optimal solution, the browser would start downloading your scripts as soon as possible, while at the same time parsing the rest of your document.

The modern approach

Today, browsers support the async and defer attributes on scripts. These attributes tell the browser it’s safe to continue parsing while the scripts are being downloaded.

async

<script src="path/to/script1.js" async></script>
<script src="path/to/script2.js" async></script>

Scripts with the async attribute are executed asynchronously. This means the script is executed as soon as it’s downloaded, without blocking the browser in the meantime.
This implies that it’s possible that script 2 is downloaded and executed before script 1.

According to http://caniuse.com/#feat=script-async, 97.78% of all browsers support this.

defer

<script src="path/to/script1.js" defer></script>
<script src="path/to/script2.js" defer></script>

Scripts with the defer attribute are executed in order (i.e. first script 1, then script 2). This also does not block the browser.

Unlike async scripts, defer scripts are only executed after the entire document has been loaded.

According to http://caniuse.com/#feat=script-defer, 97.79% of all browsers support this. 98.06% support it at least partially.

An important note on browser compatibility: in some circumstances, Internet Explorer 9 and earlier may execute deferred scripts out of order. If you need to support those browsers, please read this first!

(To learn more and see some really helpful visual representations of the differences between async, defer and normal scripts check the first two links at the references section of this answer)

Conclusion

The current state-of-the-art is to put scripts in the <head> tag and use the async or defer attributes. This allows your scripts to be downloaded ASAP without blocking your browser.

The good thing is that your website should still load correctly on the 2% of browsers that do not support these attributes while speeding up the other 98%.

References

30

  • 77

    I’m surprised nobody cited Google’s explanation… developers.google.com/speed/docs/insights/BlockingJS

    Aug 15, 2014 at 21:27


  • 8

    I’m not clear on what touches the DOM and what doesn’t. Can you clarify? Is it safe to do an async load on something like jquery.js?

    – Doug

    Sep 6, 2014 at 23:11

  • 7

    @Doug For example document.write operates on the dom. The question isn’t if a script manipulates the dom, but when it does. As long as all dom manipulation happens after domready event has triggered, you’re ok. jQuery is a library, and as such doesn’t – or shouldn’t – manipulate the dom by itself.

    – Bart

    Sep 9, 2014 at 15:34


  • 25

    This answer is misleading. Modern browsers don’t stop parsing when they reach a synchronous script tag that may affect the HTML, they just stop rendering/executing, and continue parsing optimistically to start downloading other resources that will probably be requested subsequently if no HTML is affected.

    Oct 16, 2014 at 21:23

  • 47

    Why the async and defer attributes are not used nowhere? I mean, I viewed a lot of HTML sources from internet, and I don’t see the async and defer attributes anywhere. … ?

    May 10, 2016 at 14:02

250

Just before the closing body tag, as stated on Put Scripts at the Bottom:

Put Scripts at the Bottom

The problem caused by scripts is that they block parallel downloads. The HTTP/1.1 specification suggests that browsers download no more than two components in parallel per hostname. If you serve your images from multiple hostnames, you can get more than two downloads to occur in parallel. While a script is downloading, however, the browser won’t start any other downloads, even on different hostnames.

9

  • 7

    Agreed with the concept and its explanation. But what happens if the user starts playing with the page. Suppose I’ve an AJAX dropdown which will start loading after the page has appeared to the user but while it is loading, the user clicks it! And what if a ‘really impatient’ user submits the form?

    Jan 3, 2012 at 14:29

  • 9

    @Hermant Old comment but you may do the trick disabling the fields by default then enabling them using JS when the DOM is fully loaded. That’s what Facebook seems to be doing nowadays.

    – jmic

    Nov 11, 2012 at 6:20

  • 2

    Just tested this with chrome, to check if this is still the same. It Is. You can check the differences in page load time of your browsers here. stevesouders.com/cuzillion

    – cypher

    Jan 8, 2013 at 4:19


  • 53

    If this is best practice, why does stack overflow include all their script tags in <head>? 😛

    – Philip

    Apr 20, 2013 at 5:04

  • 10

    In some cases, especially in ajax heavy sites, loading in head can actually result in faster load times. See: encosia.com/dont-let-jquerys-document-ready-slow-you-down (note that the “live()” function is deprecated in jquery, but the article still applies with the “on()” or “delegate” function). Loading in <head> may also be needed to guarantee correct behavior as pointed out by @Hermant. Finally, modernizr.com/docs recommends placing its scripts in the <head> for reasons explained on its site.

    – Nathan

    Jun 7, 2013 at 15:54


95

Non-blocking script tags can be placed just about anywhere:

<script src="https://stackoverflow.com/questions/436411/script.js" async></script>
<script src="https://stackoverflow.com/questions/436411/script.js" defer></script>
<script src="https://stackoverflow.com/questions/436411/script.js" async defer></script>
  • async script will be executed asynchronously as soon as it is available
  • defer script is executed when the document has finished parsing
  • async defer script falls back to the defer behavior if async is not supported

Such scripts will be executed asynchronously/after document ready, which means you cannot do this:

<script src="https://stackoverflow.com/questions/436411/jquery.js" async></script>
<script>jQuery(something);</script>
<!--
  * might throw "jQuery is not defined" error
  * defer will not work either
-->

Or this:

<script src="https://stackoverflow.com/questions/436411/document.write(something).js" async></script>
<!--
  * might issue "cannot write into document from an asynchronous script" warning
  * defer will not work either
-->

Or this:

<script src="https://stackoverflow.com/questions/436411/jquery.js" async></script>
<script src="jQuery(something).js" async></script>
<!--
  * might throw "jQuery is not defined" error (no guarantee which script runs first)
  * defer will work in sane browsers
-->

Or this:

<script src="https://stackoverflow.com/questions/436411/document.getElementById(header).js" async></script>
<div id="header"></div>
<!--
  * might not locate #header (script could fire before parser looks at the next line)
  * defer will work in sane browsers
-->

Having said that, asynchronous scripts offer these advantages:

  • Parallel download of resources:
    Browser can download stylesheets, images and other scripts in parallel without waiting for a script to download and execute.
  • Source order independence:
    You can place the scripts inside head or body without worrying about blocking (useful if you are using a CMS). Execution order still matters though.

It is possible to circumvent the execution order issues by using external scripts that support callbacks. Many third party JavaScript APIs now support non-blocking execution. Here is an example of loading the Google Maps API asynchronously.

5

  • 2

    This is the correct answer for today – using this approach means it’s easier to keep your widgets self contained, no need to do fancy <head> include logic.

    Jul 7, 2015 at 18:17

  • 1

    I’m confused why you can’t use async or defer when including jQuery as you specify in your second block: <script src="jquery.js" async></script>. Are you able to explain why? I thought I need to have the async tag in for performance—per the accepted answer—so my page can load even while jQuery is still loading]. Thanks!

    Aug 6, 2015 at 8:10

  • 3

    @elbow 99% of times <script src=jquery.js> is followed by $(function(){ ... }) blocks somewhere in the page. Asynchronous loading does not guarantee that jQuery will be loaded at the time browser tries to parse those blocks hence it will raise $ is not defined error (you may not get the error if jQuery was loaded from cache). I answered a question about loading jQuery asynchronously and preserve $(function(){ ... }). I’ll see if I could find it, or you can look at this question: stackoverflow.com/q/14811471/87015

    – Salman A

    Aug 6, 2015 at 9:12


  • 1

    @SalmanA Thank you! Yes, I fall in that 99%. I first need jquery lib to load, then my remaining .js scripts. When I declare async or defer on the jquery lib script tag, my .js scripts don’t work. I thought $(function(){ ... })protected that—guess not. Current solution: I don’t add defer or async on jquery lib script, but I do add async on my follow up .js scripts. Note: the reason I’m doing any of this is to make Google Page Speed happy. Thx again for the help! Any other advice is welcome. (Or a link to your previous answer). 🙂

    Aug 6, 2015 at 21:22

  • @elbow See stackoverflow.com/a/21013975/87015, it will only give you an idea but not the complete solution. You could instead search for “jquery async loader libraries”.

    – Salman A

    Aug 7, 2015 at 5:40