32

I always put my script tags at the bottom of the page since it's good practice to load scripts after things like HTML/CSS and text has finished loading. I just found out about the defer attribute which basically does the same thing, that is it waits till the page has finished loading before fetching and executing scripts.

So if using the defer attribute is it necessary to physically place script tags at the bottom of the page vs inside the head tag?

I find it better for readability to keep script tags inside the head section.

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

or

<script defer="defer">

// do something

</script>
  • 12
    Your second example won't actually be deferred. `defer` only works on external scripts. – millimoose Sep 13 '12 at 23:42
  • Wouldn't putting the script at the end _and_ setting it to defer be redundant? As for whether or not to use `defer` at all, do you want to ignore Opera's market share? (Apparently Opera doesn't support the html5 `async` attribute either.) You might want to consider something like [yepnope.js](http://yepnopejs.com/). – nnnnnn Sep 13 '12 at 23:47
  • The corrolary is that if you need to use some literal scripts then the order of execution of the deferred external scripts and the literal scripts will suddenly be nontrivial to determine when reading the HTML source. What you'd have to do is make sure ALL scripts (except Modernizr and other that should be loaded early) are loaded from external files, and declare them as deferred. This doesn't really seem like that much less bother than just making sure scripts are included at the end of a file. Sure it's cleaner, but it's understandable why someone would pick the other option. – millimoose Sep 13 '12 at 23:48
  • @millimoose In fact, it worked on internal scripts too in Gecko 1.9.1- – raina77ow Sep 13 '12 at 23:48
  • @millimoose - this is not limited to external scripts. http://www.w3.org/TR/html4/interact/scripts.html#h-18.2.1 – Dave R. Sep 13 '12 at 23:51
  • @raina77ow It's entirely possible, but it's not specified: http://www.whatwg.org/specs/web-apps/current-work/multipage/scripting-1.html#attr-script-defer, and thus not something I'd want to bet on. – millimoose Sep 13 '12 at 23:53
  • @DaveR. The HTML 4 spec is woefully ambiguous on how and when such script elements *should* be evaluated though. All it seems to say is that such an element may be executed at some unspecified time after it's encountered. – millimoose Sep 13 '12 at 23:56
  • 3
    Essentially, my point is that if you keep undeferred scripts one the bottom of the page, you know when they'll be executed, you know in what order they'll be executed, and you know what the performance impact of this will be. If you use deferred scripts in ``, you're relying on implementation details / adherence to HTML5 of the specific browser. – millimoose Sep 13 '12 at 23:58
  • @millimoose that'a a good point. –  Sep 14 '12 at 00:00

5 Answers5

11

The current best practice? Use deferred scripts in order in the head, unless you need to support older browsers (IE < 10, Opera Mini, etc.) - 97.45% browser usage (ref)

Why? With defer, parsing finishes just like when we put the script at the end of the body tag, but overall the script execution finishes well before, because the script has been downloaded in parallel with the HTML parsing. This scenario will trigger the faster domInteractive event that is used for page loading speed. With async, the order in which your script will execute varies based on how fast the script is fetched, so order can be compromised. Futhermore, async scripts are executed inline and pause the parsing of the HTML.

Ben Sewards
  • 2,571
  • 2
  • 25
  • 43
  • 1
    Browsers already can and will render partially loaded HTML even without a hint as to whether a specific element will or will not generate content. To put this another way, if you add a ` – millimoose Sep 14 '12 at 00:09
  • 1
    For a silly example, see: https://dl.dropbox.com/u/263227/test.html. The page is done rendering a fair while before the `alert()` executes. The mere presence of an undeferred ` – millimoose Sep 14 '12 at 00:17
  • @millimoose thank you for the example and I do believe we all hate to wait – Ben Sewards Sep 15 '12 at 23:27
  • The question was whether it's necessary to put a ` – ryboe Aug 19 '14 at 04:56
  • Yes, yes, yes! This is the right answer. Put them in the head and mark them with defer. Here's more: http://www.html5rocks.com/en/tutorials/speed/script-loading/ – Rap Feb 25 '15 at 13:31
  • Wow great article @Rap,looks like defer helps execute the script just before DOMContentLoaded. Even though it's just not supported in IE8, defer is only picked up in IE9+! – Ben Sewards Feb 25 '15 at 20:15
  • This answer is wrong on two levels: 1) It doesn't answer the question of whether deferred scripts should _also_ be physically placed at the bottom of the page. And 2) it claims setting `defer` is a promise to not use `document.write`. You _can_ in fact use `document.write` with a deferred script, because deferred scripts are processed synchronously and sequentially immediately before `DOMContentLoaded`. `async` scripts, on the other hand, shouldn't use `document.write`. – crenshaw-dev Jun 20 '18 at 14:08
  • 1
    @mac9416 this answer was written back in 2012 and clearly was not maintained. I will go ahead and update my answer in order to keep it relevant. – Ben Sewards Jun 20 '18 at 14:39
  • 2
    @BenSewards just noticed that this is still off: "using this alternative attribute indicates that the script does not contain document.write". That is only true for `async`. Deferred scripts can _definitely_ use `document.write`, because they execute synchronously and sequentially after the rest of the DOM is loaded. `async` scripts should _not_ use `document.write`, because it's not predictable where that write will occur. – crenshaw-dev Jun 06 '19 at 21:04
8

Best practices have shifted since these answers were written, because support for the defer attribute has grown to 98% globally.

Unless you need to optimize speed for older browsers, you should put the script in the head and mark as defer. This 1) keeps all your script references in one place (more maintainable) and 2) makes the browser aware of the script sooner, which lets it start prioritizing resources earlier.

The performance difference difference should be negligible for most pages, because the browser's pre-loader probably isn't going to start downloading a deferred script until the whole document is parsed anyway. But, it shouldn't hurt, and it leaves more work for the browser, which is generally best.

crenshaw-dev
  • 7,504
  • 3
  • 45
  • 81
2

First of all, the defer attribute is not supported by all browsers (and some that do support it just ignore it). Putting the script at the bottom of the page ensures that all HTML elements above it have been loaded into the DOM before the script executes. An alternative is using the onload method or using jQuery's DOM ready function.

Will
  • 19,661
  • 7
  • 47
  • 48
  • Onload and domready are different I believe. Onload will fire once everything on a page has completely finished loading while domready fires as soon as the dom is ready to be manipulated even if some things haven't finished loading. –  Sep 13 '12 at 23:50
  • `onload/ready` is not an alternative, as it still requires that the script loads before continuing with the page. – zzzzBov Sep 13 '12 at 23:50
  • 1
    As of 2/2015, defer is supported on all browsers, desktop and mobile except Opera Mini. Please start using it. – Rap Feb 25 '15 at 13:29
2

There are different reasons why using defer in a script at the bottom of the HTML makes sense:

  • Not all browsers support "defer". If you put your script in the HEAD with defer and the browser does not support defer, the script blocks the parallel download of the following elements and also blocks progressive rendering for all content below the script.
  • If you just place the script at the bottom without defer the browser will continue to show a busy indicator until the page has finished parsing the JavaScript.
  • In some cases the "script at the bottom without defer" blocks progressive rendering. Tested in Google Chrome 36 and IE11 (see comment below)

It's important to know that every browser handels things like "defer" and also the busy indicators a little bit different.

Best practice should be: Put scripts at the bottom with defer.

Besides the readability aspect I only see advantages by putting scripts at the bottom with defer in comparison to "Script in Head with defer" or "Script at the bottom without defer".

BITS
  • 121
  • 1
  • 11
  • Here you can also see the clear difference between "Script at the bottom without defer" and "Script at the bottom with defer" in Google Chrome. In this test the "Script at the bottom without defer" is blocking the progressive rendering at a specific point. http://www.webpagetest.org/video/compare.php?tests=140821_W4_A3K,140821_DY_9R6,140821_97_9PB,140821_MM_9N0 – BITS Aug 21 '14 at 08:16
  • Scripts *can* go in the body according to W3C, but semantically they belong in the head. #soapBox #nitPicking – Rap Feb 25 '15 at 17:17
  • @BITS - your link is dead – vsync Dec 21 '16 at 19:52
1

While I agree with you that using 'defer' and placing scripts in header will improve readability, this attribute is still not supported by both desktop and mobile Opera (check this table for details).

raina77ow
  • 103,633
  • 15
  • 192
  • 229
  • 1
    Opera desktop is now fully supported. Safe to use except for your mobile Opera users (both of them). – Rap Feb 25 '15 at 17:12