How exactly does

However, it alerts 2 - 1 - 3. Why doesn't it alert 1 - 2 - 3?


Solution 1:

UPDATED: 2/19/2016

Consider this answer outdated. Refer to other answers on this post for information relevant to newer browser version.

Basically, defer tells the browser to wait "until it's ready" before executing the javascript in that script block. Usually this is after the DOM has finished loading and document.readyState == 4

The defer attribute is specific to internet explorer. In Internet Explorer 8, on Windows 7 the result I am seeing in your JS Fiddle test page is, 1 - 2 - 3.

The results may vary from browser to browser.

Contrary to popular belief IE follows standards more often than people let on, in actuality the "defer" attribute is defined in the DOM Level 1 spec

The W3C's definition of defer:

"When set, this boolean attribute provides a hint to the user agent that the script is not going to generate any document content (e.g., no "document.write" in javascript) and thus, the user agent can continue parsing and rendering."

Solution 2:

A few snippets from the HTML5 spec:

The defer and async attributes must
not be specified if the src attribute
is not present.

There are three possible modes that
can be selected using these
attributes [async and defer]. If the async attribute is
present, then the script will be
executed asynchronously, as soon as it
is available. If the async attribute
is not present but the defer attribute
is present, then the script is
executed when the page has finished
parsing. If neither attribute is
present, then the script is fetched
and executed immediately, before the
user agent continues parsing the page.

The exact processing details for these
attributes are, for mostly historical
reasons, somewhat non-trivial,
involving a number of aspects of HTML.
The implementation requirements are
therefore by necessity scattered
throughout the specification. The
algorithms below (in this section)
describe the core of this processing,
but these algorithms reference and are
referenced by the parsing rules for
script start and end tags in HTML, in
foreign content, and in XML, the rules
for the document.write() method, the
handling of scripting, etc.

If the element has a src attribute,
and the element has a defer attribute,
and the element has been flagged as
"parser-inserted", and the element
does not have an async attribute:

The element must be added to the end of the list of scripts that will
execute when the document has finished
parsing associated with the Document
of the parser that created the

Solution 3:

The real answer is: Because you cannot trust defer.

In concept, defer and async differ as follows:

async allows the script to be downloaded in the background without blocking. Then, the moment it finishes downloading, rendering is blocked and that script executes. Render resumes when the script has executed.

defer does the same thing, except claims to guarantee that scripts execute in the order they were specified on the page, and that they will be executed after the document has finished parsing. So, some scripts may finish downloading then sit and wait for scripts that downloaded later but appeared before them.

Unfortunately, due to what is really a standards cat fight, defer's definition varies spec to spec, and even in the most recent specs doesn't offer a useful guarantee. As answers here and this issue demonstrate, browsers implement defer differently:

  • In certain situations some browsers have a bug that causes defer scripts to run out of order.
  • Some browsers delay the DOMContentLoaded event until after the defer scripts have loaded, and some don't.
  • Some browsers obey defer on <script> elements with inline code and without a src attribute, and some ignore it.

Fortunately the spec does at least specify that async overrides defer. So you can treat all scripts as async and get a wide swath of browser support like so:

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

98% of browsers in use worldwide and 99% in the US will avoid blocking with this approach.

(If you need to wait until the document has finished parsing, listen to the event DOMContentLoaded event or use jQuery's handy .ready() function. You'd want to do this anyway to fall back gracefully on browsers that don't implement defer at all.)

Solution 4:

defer can only be used in <script> tag for external script inclusion. Hence it is advised to be used in the <script>-tags in the <head>-section.

Solution 5:

As defer attribute works only with scripts tag with src. Found a way to mimic defer for inline scripts. Use DOMContentLoaded event.

<script defer src="external-script.js"></script>
document.addEventListener("DOMContentLoaded", function(event) {
    // Your inline scripts which uses methods from external-scripts.

This is because, DOMContentLoaded event fires after defer attributed scripts are completely loaded.

Solution 6:

The defer attribute is only for external scripts (should only be used if the src attribute is present).

Related:  Add Whatsapp function to website, like sms, tel