New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

script[defer] doesn't work in IE<=9 #42

Open
paulirish opened this Issue Aug 10, 2011 · 56 comments

Comments

Projects
None yet
@paulirish
Member

paulirish commented Aug 10, 2011

(Edited 2012.03.01)

TL;DR: don't use defer for external scripts that can depend on eachother if you need IE <= 9 support

There is a bug in IE<=9 (confirmed, below, by an IE engineer) where if you have two scripts such as...

<script defer src="jquery.js"></script>
<script defer src="jquery-ui.js"></script>

And the first script modifies the dom with appendChild, innerHTML, (etc.), the second script can start executing before the first one has finished. Thus, a dependency between the two will break.

The details of this limitation begin at this comment

This essentially means that script[defer] cannot be used in most cases unless you have dropped IE8 and IE9 support. If, however, you can UA sniff to serve script[defer] to all browsers except IE6-9, that will net you large performance wins.

Steve Souders indicated there may be a hack of inserting an empty <script></script> tag between the two tags that may address this problem. Research to be done…

original post follows:




# comprehensive research and article on script @defer

@defer scripts execute when the browser gets around to them, but they execute in order. this is awesome for performance.
it's also awesome that it's been in IE since IE5.

but, we're lacking a little bit of comprehensive research on this..

kyle simpson thinks there may be some edge case issues with defer... from this h5bp thread...

  1. support of defer on dynamic script elements isn't defined or supported in any browser... only works for script tags in the markup. this means it's completely useless for the "on-demand" or "lazy-loading" techniques and use-cases.
  2. i believe there was a case where in some browsers defer'd scripts would start executing immediately before DOM-ready was to fire, and in others, it happened immediately after DOM-ready fired. Will need to do more digging for more specifics on that.
  3. defer used on a script tag referencing an external resource behaved differently than defer specified on a script tag with inline code in it. That is, it couldn't be guaranteed to work to defer both types of scripts and have them still run in the correct order.
  4. defer on a script tag written out by a document.write() statement differed from a script tag in markup with @defer.

it'd be excellent to get a great summary of the full story across browsers and these issues so we can use defer confidently.

see also:


- @aaronpeters @Schepp
@paulirish

This comment has been minimized.

Show comment
Hide comment
@paulirish

paulirish Aug 10, 2011

Member

kyle added....

To answer @paulirish's earlier question (https://github.com/paulirish/html5-boilerplate/issues/28#issuecomment-1765361) about defer quirks, look at how "DOMContentLoaded" behaves across IE, Chrome, and Firefox in the defer test.

In IE9 and Chrome15, the DOMContentLoaded event is held up (blocked) and not fired until after the scripts run. In FF, however, the DOMContentLoaded event is not held up, it fires right away, and the scripts start executing after it. That's a giant inconsistency across modern browsers, and one of the reasons why I don't think defer is sufficient.

Member

paulirish commented Aug 10, 2011

kyle added....

To answer @paulirish's earlier question (https://github.com/paulirish/html5-boilerplate/issues/28#issuecomment-1765361) about defer quirks, look at how "DOMContentLoaded" behaves across IE, Chrome, and Firefox in the defer test.

In IE9 and Chrome15, the DOMContentLoaded event is held up (blocked) and not fired until after the scripts run. In FF, however, the DOMContentLoaded event is not held up, it fires right away, and the scripts start executing after it. That's a giant inconsistency across modern browsers, and one of the reasons why I don't think defer is sufficient.

@getify

This comment has been minimized.

Show comment
Hide comment
@getify

getify Aug 10, 2011

To give more context to the above list of defer issues:

  1. defer doesn't have any meaning in dynamic script loading. But it doesn't need to, because of the new "ordered async" (async=false) that IS spec'd and now in almost all browsers' current releases. What's confusing though is that you have to use defer when you're dealing with markup, and async=false when you're dynamically creating script elements. The latter would make you assume you could/should use async in markup, but that's not going to work because order is not preserved -- unless of course you happen to not care about order.
  2. this is now proven (partially documented) by this video: http://www.screenr.com/icxs To save you from having to watch it, though, IE9 and Chrome15 both block the DOMContentLoaded event (aka, "DOM-ready") until after all the defer scripts finish, whereas FF8(nightly) does not block the event. I'm willing to bet there are other browsers which fall on both sides of that issue, as well, as the spec seems a bit confusing on this particular point (at least in my reading).
  3. It seems from some older blog posts that at one point, inline script blocks could have defer set on them, and browsers would respect that. http://hacks.mozilla.org/2009/06/defer/ However, as that video above clearly illustrates, none of the current browsers respect that, and in fact, reading the spec, defer is NOT defined for inline script blocks. That makes it uber-difficult to convert an existing set of script tags (some external, some inline) to use defer, if those inline blocks are relying on ordering (almost always they are).
  4. I don't have much evidence of this, but I know a guy who's done a bunch on this, and I'll ping him to get some more specifics. I stay far the hell away from document.write(), but some unfortunate souls have to deal with that reality (aka, "nightmare").

getify commented Aug 10, 2011

To give more context to the above list of defer issues:

  1. defer doesn't have any meaning in dynamic script loading. But it doesn't need to, because of the new "ordered async" (async=false) that IS spec'd and now in almost all browsers' current releases. What's confusing though is that you have to use defer when you're dealing with markup, and async=false when you're dynamically creating script elements. The latter would make you assume you could/should use async in markup, but that's not going to work because order is not preserved -- unless of course you happen to not care about order.
  2. this is now proven (partially documented) by this video: http://www.screenr.com/icxs To save you from having to watch it, though, IE9 and Chrome15 both block the DOMContentLoaded event (aka, "DOM-ready") until after all the defer scripts finish, whereas FF8(nightly) does not block the event. I'm willing to bet there are other browsers which fall on both sides of that issue, as well, as the spec seems a bit confusing on this particular point (at least in my reading).
  3. It seems from some older blog posts that at one point, inline script blocks could have defer set on them, and browsers would respect that. http://hacks.mozilla.org/2009/06/defer/ However, as that video above clearly illustrates, none of the current browsers respect that, and in fact, reading the spec, defer is NOT defined for inline script blocks. That makes it uber-difficult to convert an existing set of script tags (some external, some inline) to use defer, if those inline blocks are relying on ordering (almost always they are).
  4. I don't have much evidence of this, but I know a guy who's done a bunch on this, and I'll ping him to get some more specifics. I stay far the hell away from document.write(), but some unfortunate souls have to deal with that reality (aka, "nightmare").
@jdalton

This comment has been minimized.

Show comment
Hide comment
@jdalton

jdalton Aug 10, 2011

I made a test which seems to confirm the following:

In FF, however, the DOMContentLoaded event is not held up, it fires right away, and the scripts start executing after it. That's a giant inconsistency across modern browsers, and one of the reasons why I don't think defer is sufficient.

http://dl.dropbox.com/u/513327/domload_defer.html (load and reload it in Firefox 5 and then load in Chrome)

Chrome 12 results: expected: number; got: number;
Firefox 6, 5, 4, 3.6 results: expected: number; got: undefined;
Firefox 3.5, 3.0 results: expected: number; got: number;

Update: I removed the Cuzillion tests on visual rendering blocking because they were invalid.

jdalton commented Aug 10, 2011

I made a test which seems to confirm the following:

In FF, however, the DOMContentLoaded event is not held up, it fires right away, and the scripts start executing after it. That's a giant inconsistency across modern browsers, and one of the reasons why I don't think defer is sufficient.

http://dl.dropbox.com/u/513327/domload_defer.html (load and reload it in Firefox 5 and then load in Chrome)

Chrome 12 results: expected: number; got: number;
Firefox 6, 5, 4, 3.6 results: expected: number; got: undefined;
Firefox 3.5, 3.0 results: expected: number; got: number;

Update: I removed the Cuzillion tests on visual rendering blocking because they were invalid.

@Schepp

This comment has been minimized.

Show comment
Hide comment
@Schepp

Schepp Aug 10, 2011

Isn't blocking the visual rendering only supposed to occur with non-defered scripts? What would be the advantage of defer then? I'd say all is well with how FF 3.5+ and Safari handle it. Safari 4 and sorts blocking may just be indication that they don't recognize a defer-attribute yet.

In regards to DOMContentLoaded event being triggered too early, maybe the following manual DOMContentLoaded triggering technique may be of interest for a fix: http://stackoverflow.com/questions/942921/lazy-loading-the-addthis-script-or-lazy-loading-external-js-content-dependent-o

if( document.createEvent ) {
 var evt = document.createEvent("MutationEvents"); 
 evt.initMutationEvent("DOMContentLoaded", true, true, document, "", "", "", 0); 
 document.dispatchEvent(evt);
}

Schepp commented Aug 10, 2011

Isn't blocking the visual rendering only supposed to occur with non-defered scripts? What would be the advantage of defer then? I'd say all is well with how FF 3.5+ and Safari handle it. Safari 4 and sorts blocking may just be indication that they don't recognize a defer-attribute yet.

In regards to DOMContentLoaded event being triggered too early, maybe the following manual DOMContentLoaded triggering technique may be of interest for a fix: http://stackoverflow.com/questions/942921/lazy-loading-the-addthis-script-or-lazy-loading-external-js-content-dependent-o

if( document.createEvent ) {
 var evt = document.createEvent("MutationEvents"); 
 evt.initMutationEvent("DOMContentLoaded", true, true, document, "", "", "", 0); 
 document.dispatchEvent(evt);
}
@jdalton

This comment has been minimized.

Show comment
Hide comment
@jdalton

jdalton Aug 10, 2011

@Schepp

In regards to DOMContentLoaded event being triggered too early, maybe the following manual DOMContentLoaded triggering technique may be of interest for a fix

I think that might cause problems with some handlers as it's generally assumed DOMContentLoaded is only fired once.

jdalton commented Aug 10, 2011

@Schepp

In regards to DOMContentLoaded event being triggered too early, maybe the following manual DOMContentLoaded triggering technique may be of interest for a fix

I think that might cause problems with some handlers as it's generally assumed DOMContentLoaded is only fired once.

@mathiasbynens

This comment has been minimized.

Show comment
Hide comment
@mathiasbynens

mathiasbynens Aug 11, 2011

Member

FWIW:

[22:36] <matjas> is there a point in using @defer when you only use a single <script> and it’s at the bottom, right before </body>?
[22:36] <Hixie> not really
[22:38] <matjas> not really or not at all?
[22:38] <matjas> what is the point?
[22:39] <Hixie> there's no point that i can think of
[22:39] <Ms2ger> Being fancy! :)
[22:39] <Hixie> there are some subtle minor differences, but nothing useful i don't think

Source

Member

mathiasbynens commented Aug 11, 2011

FWIW:

[22:36] <matjas> is there a point in using @defer when you only use a single <script> and it’s at the bottom, right before </body>?
[22:36] <Hixie> not really
[22:38] <matjas> not really or not at all?
[22:38] <matjas> what is the point?
[22:39] <Hixie> there's no point that i can think of
[22:39] <Ms2ger> Being fancy! :)
[22:39] <Hixie> there are some subtle minor differences, but nothing useful i don't think

Source

@aaronpeters

This comment has been minimized.

Show comment
Hide comment
@aaronpeters

aaronpeters Aug 11, 2011

Are the logical next steps to:
a) define and agree on the test cases?
b) define and agree on the testing methodology?
c) create solid test pages
d) do the testing

aaronpeters commented Aug 11, 2011

Are the logical next steps to:
a) define and agree on the test cases?
b) define and agree on the testing methodology?
c) create solid test pages
d) do the testing

@aaronpeters

This comment has been minimized.

Show comment
Hide comment
@aaronpeters

aaronpeters Aug 11, 2011

@jdalton

I ran your DCL test page (http://dl.dropbox.com/u/513327/domload_defer.html) in IE9: expected: number; got: number;

aaronpeters commented Aug 11, 2011

@jdalton

I ran your DCL test page (http://dl.dropbox.com/u/513327/domload_defer.html) in IE9: expected: number; got: number;

@Schepp

This comment has been minimized.

Show comment
Hide comment
@Schepp

Schepp Aug 11, 2011

The question is: What is our goal here (in regards to H5BP)? Upgrading all scripts which are already aligned at the document's end with defer? Even if we wouldn't have a DOMContentLoaded discrepancy between browsers we would not gain anything performance-wise. deferreally makes sense when you have like a stubborn CMS that cannot queue scripts for an insertion at the very end. But then again, you cannot generally auto-deferall scripts that you come across as they might contain a document.write or they are accompanied by some (officially) non-deferable inline-script. So the main problem is that even if all browsers would follow one standard, it will never be a no-brainer solution.

What we could do is do some tests just for fun and curiosity (which might be reason enough ;)

Schepp commented Aug 11, 2011

The question is: What is our goal here (in regards to H5BP)? Upgrading all scripts which are already aligned at the document's end with defer? Even if we wouldn't have a DOMContentLoaded discrepancy between browsers we would not gain anything performance-wise. deferreally makes sense when you have like a stubborn CMS that cannot queue scripts for an insertion at the very end. But then again, you cannot generally auto-deferall scripts that you come across as they might contain a document.write or they are accompanied by some (officially) non-deferable inline-script. So the main problem is that even if all browsers would follow one standard, it will never be a no-brainer solution.

What we could do is do some tests just for fun and curiosity (which might be reason enough ;)

@robflaherty

This comment has been minimized.

Show comment
Hide comment
@robflaherty

robflaherty Aug 11, 2011

Isn't the visual rendering blocking/non-blocking that @jdalton reported expected? The report HTML on the Cuzillion page comes after the external script. So doesn't it make sense that it would be blocked without defer and not blocked with defer?

robflaherty commented Aug 11, 2011

Isn't the visual rendering blocking/non-blocking that @jdalton reported expected? The report HTML on the Cuzillion page comes after the external script. So doesn't it make sense that it would be blocked without defer and not blocked with defer?

@mathiasbynens

This comment has been minimized.

Show comment
Hide comment
@mathiasbynens

mathiasbynens Aug 11, 2011

Member

@robflaherty Good point. This:

…appears after the last <script> in the test page HTML, so it’s not really a test case of <script defer src=foo></body>.

Member

mathiasbynens commented Aug 11, 2011

@robflaherty Good point. This:

…appears after the last <script> in the test page HTML, so it’s not really a test case of <script defer src=foo></body>.

@getify

This comment has been minimized.

Show comment
Hide comment
@getify

getify Aug 11, 2011

If the defer attribute were defined that it should push the scripts to start executing immediately after it fired the DOMContentLoaded event (like it does in FF), then defer would be useful even at the end of the body, because drastically speeding up DOM-ready is quite effective in improving the "perceived performance" of a site, which makes users think the site actually did load quicker, even if it loaded slower overall.

As it stands, defer seems somewhat more useful in FF than in IE9 and Chrome15.

getify commented Aug 11, 2011

If the defer attribute were defined that it should push the scripts to start executing immediately after it fired the DOMContentLoaded event (like it does in FF), then defer would be useful even at the end of the body, because drastically speeding up DOM-ready is quite effective in improving the "perceived performance" of a site, which makes users think the site actually did load quicker, even if it loaded slower overall.

As it stands, defer seems somewhat more useful in FF than in IE9 and Chrome15.

@artzstudio

This comment has been minimized.

Show comment
Hide comment
@artzstudio

artzstudio Aug 11, 2011

If "defer" is made the default, will developers get confused that their inline JS is processed before the deferred (external) scripts?

http://www.artzstudio.com/files/Boot/test/benchmarks/script.defer.html

Most sites have a need for inline JS, for example Google Analytics code, page specific initialization, etc.

artzstudio commented Aug 11, 2011

If "defer" is made the default, will developers get confused that their inline JS is processed before the deferred (external) scripts?

http://www.artzstudio.com/files/Boot/test/benchmarks/script.defer.html

Most sites have a need for inline JS, for example Google Analytics code, page specific initialization, etc.

@robflaherty

This comment has been minimized.

Show comment
Hide comment
@robflaherty

robflaherty Aug 11, 2011

Couple of other points... it may be worth noting that stylesheet downloading blocks DOMContentLoaded only if the stylesheet is followed by scripts. Adding defer changes this and causes DOMContentLoaded to fire before the stylesheet has finished downloading. Probably not a common scenario but I thought I'd mention it.

Example: http://stevesouders.com/cuzillion/?c0=hc1hfff2_0_f&c1=bj1hfft1_0_f&t=1313073628

Another thing to keep in mind when testing is WebKit's PreloadScanner, which prefetches scripts and runs in just about every real-world scenario. In more cases it's surely tangential but there may be some wacky test cases where it affects results.

robflaherty commented Aug 11, 2011

Couple of other points... it may be worth noting that stylesheet downloading blocks DOMContentLoaded only if the stylesheet is followed by scripts. Adding defer changes this and causes DOMContentLoaded to fire before the stylesheet has finished downloading. Probably not a common scenario but I thought I'd mention it.

Example: http://stevesouders.com/cuzillion/?c0=hc1hfff2_0_f&c1=bj1hfft1_0_f&t=1313073628

Another thing to keep in mind when testing is WebKit's PreloadScanner, which prefetches scripts and runs in just about every real-world scenario. In more cases it's surely tangential but there may be some wacky test cases where it affects results.

@getify

This comment has been minimized.

Show comment
Hide comment
@getify

getify Aug 22, 2011

I've just run across an issue where I'm loading jquery and jquery-ui in succession, using script tags with defer set on them. And in IE9, this is causing a script error, because apparently IE9 is executing jquery-ui before jquery, which throws the obvious error about "jQuery is undefined".

Has anyone else seen script@defer behave wonky in IE9? I tried it in IE10p2 and it didn't error, but I don't know if that's because it's a bug that was fixed, or if that's an accident of race-condition. Probably the former, but could be the latter.

getify commented Aug 22, 2011

I've just run across an issue where I'm loading jquery and jquery-ui in succession, using script tags with defer set on them. And in IE9, this is causing a script error, because apparently IE9 is executing jquery-ui before jquery, which throws the obvious error about "jQuery is undefined".

Has anyone else seen script@defer behave wonky in IE9? I tried it in IE10p2 and it didn't error, but I don't know if that's because it's a bug that was fixed, or if that's an accident of race-condition. Probably the former, but could be the latter.

@aaronpeters

This comment has been minimized.

Show comment
Hide comment
@aaronpeters

aaronpeters Aug 23, 2011

@getify

can you run a couple of tests with the test page in some IE9 test nodes on Webpagetest.org and publish links to results here?
Txs.

aaronpeters commented Aug 23, 2011

@getify

can you run a couple of tests with the test page in some IE9 test nodes on Webpagetest.org and publish links to results here?
Txs.

@getify

This comment has been minimized.

Show comment
Hide comment
@getify

getify Aug 23, 2011

@aaronpeters:

Here's my test page: http://test.getify.com/test-ie-script-defer/

Try that in IE<=9, you get "Fail!". Try it in any other browser-type, get "Pass!". Try it in IE10p2, get "Pass!".


Here's some results, as you requested, from WPT.org

(IE9-Fail) http://www.webpagetest.org/result/110823_TH_1D7F4/

(IE8-Fail) http://www.webpagetest.org/result/110823_PD_1D7F9/

(IE7-Fail) http://www.webpagetest.org/result/110823_RE_1D7FH/

(IE6-Fail) http://www.webpagetest.org/result/110823_JK_1D7FP/

(Chrome-Pass) http://www.webpagetest.org/result/110823_XE_1D7G1/

getify commented Aug 23, 2011

@aaronpeters:

Here's my test page: http://test.getify.com/test-ie-script-defer/

Try that in IE<=9, you get "Fail!". Try it in any other browser-type, get "Pass!". Try it in IE10p2, get "Pass!".


Here's some results, as you requested, from WPT.org

(IE9-Fail) http://www.webpagetest.org/result/110823_TH_1D7F4/

(IE8-Fail) http://www.webpagetest.org/result/110823_PD_1D7F9/

(IE7-Fail) http://www.webpagetest.org/result/110823_RE_1D7FH/

(IE6-Fail) http://www.webpagetest.org/result/110823_JK_1D7FP/

(Chrome-Pass) http://www.webpagetest.org/result/110823_XE_1D7G1/

@aaronpeters

This comment has been minimized.

Show comment
Hide comment
@aaronpeters

aaronpeters Aug 24, 2011

@getify

txs Kyle. Would love to get involved and participate in further, deeper research.
Will email you.

Fyi, the IE8 waterfall (only looked at this one) shows requests being aborted.
Here is info on why this happens: http://blogs.msdn.com/b/ieinternals/archive/2011/07/18/optimal-html-head-ordering-to-avoid-parser-restarts-redownloads-and-improve-performance.aspx

aaronpeters commented Aug 24, 2011

@getify

txs Kyle. Would love to get involved and participate in further, deeper research.
Will email you.

Fyi, the IE8 waterfall (only looked at this one) shows requests being aborted.
Here is info on why this happens: http://blogs.msdn.com/b/ieinternals/archive/2011/07/18/optimal-html-head-ordering-to-avoid-parser-restarts-redownloads-and-improve-performance.aspx

@getify

This comment has been minimized.

Show comment
Hide comment
@getify

getify Aug 24, 2011

@aaronpeters:

yeah, the IE8 waterfall does indeed show the requests being canceled, which is inexplicable to me because the structure of the test document is exactly as the article you linked to prescribes (that is, the charset declaration is the first tag in the head, as it should be).

Moreover, the IE6,7, and 9 waterfalls do NOT show the canceled loads, so the canceled load is most likely not the culprit (although is certainly a performance concern).

Even in a canceled load/reload of a script, as the IE8 waterfall shows, one would still expect the browser to hold off on running the correctly downloaded script ("init.js") until it could re-request and successfully download the other two and run them. No matter how you slice it, not running them in order is a failure.

Order preservation is well-defined in the spec for defer -- there's no question that it should be preserving order. As far as I can tell, there is ZERO benefit to defer (as opposed to async) if it doesn't preserve order -- isn't that basically the whole point? So I consider this an example of a failed implementation of defer (long standing too).

If IE10 indeed has fixed this (it seems so, but not confirmed), I won't use defer until IE10 is in use by 95%+ of the IE users out there, so that's gonna be awhile, to say the least.

BTW, my suspicion (unconfirmed) is that maybe it has something to do with loading all 3 scripts from different domains. But I have no explanation as to why that would cause execution order to fail.

getify commented Aug 24, 2011

@aaronpeters:

yeah, the IE8 waterfall does indeed show the requests being canceled, which is inexplicable to me because the structure of the test document is exactly as the article you linked to prescribes (that is, the charset declaration is the first tag in the head, as it should be).

Moreover, the IE6,7, and 9 waterfalls do NOT show the canceled loads, so the canceled load is most likely not the culprit (although is certainly a performance concern).

Even in a canceled load/reload of a script, as the IE8 waterfall shows, one would still expect the browser to hold off on running the correctly downloaded script ("init.js") until it could re-request and successfully download the other two and run them. No matter how you slice it, not running them in order is a failure.

Order preservation is well-defined in the spec for defer -- there's no question that it should be preserving order. As far as I can tell, there is ZERO benefit to defer (as opposed to async) if it doesn't preserve order -- isn't that basically the whole point? So I consider this an example of a failed implementation of defer (long standing too).

If IE10 indeed has fixed this (it seems so, but not confirmed), I won't use defer until IE10 is in use by 95%+ of the IE users out there, so that's gonna be awhile, to say the least.

BTW, my suspicion (unconfirmed) is that maybe it has something to do with loading all 3 scripts from different domains. But I have no explanation as to why that would cause execution order to fail.

@mathiasbynens

This comment has been minimized.

Show comment
Hide comment
@mathiasbynens

mathiasbynens Aug 24, 2011

Member

FYI, hang.nodester.org now allows you to pass content for testing, e.g. http://hang.nodester.com/test.js?2000&content=window.foo%20%3D%2042; (Thanks, @remy!) This is probably useful for future tests.

I’ve attempted to simplify @getify’s test case here: http://jsbin.com/mathias/inebat Oddly, it seems to pass for me in IE8. Did I do anything wrong?

Update: See @nicjansma’s explaination below. This test passes in IE8 because I didn’t use any code that triggers HTML parsing.

Member

mathiasbynens commented Aug 24, 2011

FYI, hang.nodester.org now allows you to pass content for testing, e.g. http://hang.nodester.com/test.js?2000&content=window.foo%20%3D%2042; (Thanks, @remy!) This is probably useful for future tests.

I’ve attempted to simplify @getify’s test case here: http://jsbin.com/mathias/inebat Oddly, it seems to pass for me in IE8. Did I do anything wrong?

Update: See @nicjansma’s explaination below. This test passes in IE8 because I didn’t use any code that triggers HTML parsing.

@getify

This comment has been minimized.

Show comment
Hide comment
@getify

getify Aug 24, 2011

FYI: I'm now not convinced (entirely) that this is IE's fault. I put a console.log() at the top of both jquery, and jquery-ui (both hosted locally now) and they "run" in the right order, but the window.jQuery variable is not defined when jQuery-ui runs, as it should be. It suggests that somehow, some way, jQuery is delaying it's initialization of the window.jQuery variable reference. I can't explain it any further than that at the moment. If anyone has any bright ideas, please do share.

getify commented Aug 24, 2011

FYI: I'm now not convinced (entirely) that this is IE's fault. I put a console.log() at the top of both jquery, and jquery-ui (both hosted locally now) and they "run" in the right order, but the window.jQuery variable is not defined when jQuery-ui runs, as it should be. It suggests that somehow, some way, jQuery is delaying it's initialization of the window.jQuery variable reference. I can't explain it any further than that at the moment. If anyone has any bright ideas, please do share.

@getify

This comment has been minimized.

Show comment
Hide comment
@getify

getify Aug 25, 2011

OK, this is officially one of the weirdest WTF's I've ever seen... Check this out (in any IE<=9):

http://test.getify.com/test-ie-script-defer/index-2.html

Look closely at the log box there. It says:

jquery.js top of the file
jquery-ui.js top of the file
ReferenceError: 'jQuery' is undefined
jquery-ui.js bottom of the file
jquery.js bottom of the file

In other words, it appears that IE is suspending execution of jQuery somewhere mid-file, switching over to execute jQuery-UI, finishing it, THEN switching back to jQuery to finish it. I'm really quite shocked at this. What happened to single-threaded "run-to-the-end" JavaScript?

getify commented Aug 25, 2011

OK, this is officially one of the weirdest WTF's I've ever seen... Check this out (in any IE<=9):

http://test.getify.com/test-ie-script-defer/index-2.html

Look closely at the log box there. It says:

jquery.js top of the file
jquery-ui.js top of the file
ReferenceError: 'jQuery' is undefined
jquery-ui.js bottom of the file
jquery.js bottom of the file

In other words, it appears that IE is suspending execution of jQuery somewhere mid-file, switching over to execute jQuery-UI, finishing it, THEN switching back to jQuery to finish it. I'm really quite shocked at this. What happened to single-threaded "run-to-the-end" JavaScript?

@getify

This comment has been minimized.

Show comment
Hide comment
@getify

getify Aug 25, 2011

clarification: i wrapped a try/catch around the entire contents of jquery-ui.js because of that "reference" error, so what's actually happening is that jquery-UI is stopping with the error almost immediately, being caught in the try/catch, and at least allowing the final console.log(...) in jquery-ui.js to run, so we see when that file itself is done running.

getify commented Aug 25, 2011

clarification: i wrapped a try/catch around the entire contents of jquery-ui.js because of that "reference" error, so what's actually happening is that jquery-UI is stopping with the error almost immediately, being caught in the try/catch, and at least allowing the final console.log(...) in jquery-ui.js to run, so we see when that file itself is done running.

@getify

This comment has been minimized.

Show comment
Hide comment
@getify

getify Aug 25, 2011

Side note on script-defer from a bit earlier in the thread... According to spec, script-defer is NOT defined for inline script blocks, contrary to popular belief:

http://www.whatwg.org/specs/web-apps/current-work/multipage/scripting-1.html#attr-script-defer

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

getify commented Aug 25, 2011

Side note on script-defer from a bit earlier in the thread... According to spec, script-defer is NOT defined for inline script blocks, contrary to popular belief:

http://www.whatwg.org/specs/web-apps/current-work/multipage/scripting-1.html#attr-script-defer

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

This comment has been minimized.

Show comment
Hide comment
@artzstudio

artzstudio Aug 25, 2011

That's what I was trying to say earlier in the thread. Race conditions can exist if you defer external scripts required by inline scripts.

With defer: http://www.artzstudio.com/files/defer-test/defer.html

Without: http://www.artzstudio.com/files/defer-test/normal.html

artzstudio commented Aug 25, 2011

That's what I was trying to say earlier in the thread. Race conditions can exist if you defer external scripts required by inline scripts.

With defer: http://www.artzstudio.com/files/defer-test/defer.html

Without: http://www.artzstudio.com/files/defer-test/normal.html

@Schepp

This comment has been minimized.

Show comment
Hide comment
@Schepp

Schepp Aug 25, 2011

Yea, that's why I also stated "some (officially) non-deferable inline-script." <- no defer allowed :)

Kyle, maybe the bug you see results from the new Chakra engine being able, or trying to, execute multiple scripts in parallel on multicore machines. See:

http://technet.microsoft.com/en-us/library/gg699435.aspx

The new JavaScript engine takes advantage of multiple CPU cores through Windows to interpret, compile, and run code in parallel.

and http://msdn.microsoft.com/en-us/ie/ff468705.aspx#_cfperformance

The Chakra engine interprets, compiles, and executes code in parallel and takes advantage of multiple CPU cores, when available.

I'd guess that when you bind IE9 to only one single CPU-core everything will be back to normal. You do that by opening the Task Manager (Strg + Shift + Esc), go to processes, right click the corresponding IE9 process, chose "Set Affinity", uncheck all but one checkbox. Or see here:
http://www.addictivetips.com/windows-tips/how-to-set-processor-affinity-to-an-application-in-windows/

Schepp commented Aug 25, 2011

Yea, that's why I also stated "some (officially) non-deferable inline-script." <- no defer allowed :)

Kyle, maybe the bug you see results from the new Chakra engine being able, or trying to, execute multiple scripts in parallel on multicore machines. See:

http://technet.microsoft.com/en-us/library/gg699435.aspx

The new JavaScript engine takes advantage of multiple CPU cores through Windows to interpret, compile, and run code in parallel.

and http://msdn.microsoft.com/en-us/ie/ff468705.aspx#_cfperformance

The Chakra engine interprets, compiles, and executes code in parallel and takes advantage of multiple CPU cores, when available.

I'd guess that when you bind IE9 to only one single CPU-core everything will be back to normal. You do that by opening the Task Manager (Strg + Shift + Esc), go to processes, right click the corresponding IE9 process, chose "Set Affinity", uncheck all but one checkbox. Or see here:
http://www.addictivetips.com/windows-tips/how-to-set-processor-affinity-to-an-application-in-windows/

@Schepp

This comment has been minimized.

Show comment
Hide comment
@Schepp

Schepp Aug 25, 2011

Okay, forget it. You get the bug not in IE9.

Schepp commented Aug 25, 2011

Okay, forget it. You get the bug not in IE9.

@nicjansma

This comment has been minimized.

Show comment
Hide comment
@nicjansma

nicjansma Aug 25, 2011

This is a known bug in IE9 that has been fixed in IE10.

In this case, after the page is done loading and IE starts to run the defer scripts, the first script, jQuery is run, and it starts building the .support object. During this time, it sets an .innerHTML, which (incorrectly) causes IE to think it needs to look for more defer scripts to run. IE starts executing the second defer script, jQuery UI, before the first script has completed, so the jQuery namespace isn't available.

nicjansma commented Aug 25, 2011

This is a known bug in IE9 that has been fixed in IE10.

In this case, after the page is done loading and IE starts to run the defer scripts, the first script, jQuery is run, and it starts building the .support object. During this time, it sets an .innerHTML, which (incorrectly) causes IE to think it needs to look for more defer scripts to run. IE starts executing the second defer script, jQuery UI, before the first script has completed, so the jQuery namespace isn't available.

@getify

This comment has been minimized.

Show comment
Hide comment
@getify

getify Aug 25, 2011

@nicjansma -- do you have any link about that? never heard about this bug before. Also, are you aware/can you confirm that it affects all IE<=9?

I dunno about anyone else, but that bug pretty much seals defer's fate in my mind, at least for a long while. If using defer in markup breaks in IE<=9, apparently for any script which sets innerHTML in the way that jQuery does, then it means you can't use defer until IE<=9 don't matter anymore. For some, I'm sure that's "soon", but for others, that could be years.

getify commented Aug 25, 2011

@nicjansma -- do you have any link about that? never heard about this bug before. Also, are you aware/can you confirm that it affects all IE<=9?

I dunno about anyone else, but that bug pretty much seals defer's fate in my mind, at least for a long while. If using defer in markup breaks in IE<=9, apparently for any script which sets innerHTML in the way that jQuery does, then it means you can't use defer until IE<=9 don't matter anymore. For some, I'm sure that's "soon", but for others, that could be years.

@brunoais

This comment has been minimized.

Show comment
Hide comment
@brunoais

brunoais Dec 25, 2012

I say that IE<=9 has a predictable loading order of script loading. The problem is that it does not follow the w3c spec.
Opera just ignores it, so... Still nothing to argue with.
Firefox, Chrome and Safari, in, at least, the last 4 versions follow the spec as-is about defer and async, the rest just ignores async. So, what one can really trust is the async and do the code according to the async rules. The defer is reliable to be used the same way as it was async.
So, for now, the best way to do is to make all scripts files not depend on other script files or use some simple method to create synchronization.
Personally, I use a really simple script that accumulates callbacks in proper order and then executes them at the right order. It's just a small script that would be made in 10 lines (I use it compressed in just one line).

After making some tests I also conclude that using async (and, for example, also use my script) is better in therms of speed loading the page than not using it, as long as there's more than 3 scripts. Anyway, if a connection to obtain a script fails, the async allows the page to finish loading properly and correctly without the page hanging and waiting for the timeout, even if the script is placed at the bottom of the page.

brunoais commented Dec 25, 2012

I say that IE<=9 has a predictable loading order of script loading. The problem is that it does not follow the w3c spec.
Opera just ignores it, so... Still nothing to argue with.
Firefox, Chrome and Safari, in, at least, the last 4 versions follow the spec as-is about defer and async, the rest just ignores async. So, what one can really trust is the async and do the code according to the async rules. The defer is reliable to be used the same way as it was async.
So, for now, the best way to do is to make all scripts files not depend on other script files or use some simple method to create synchronization.
Personally, I use a really simple script that accumulates callbacks in proper order and then executes them at the right order. It's just a small script that would be made in 10 lines (I use it compressed in just one line).

After making some tests I also conclude that using async (and, for example, also use my script) is better in therms of speed loading the page than not using it, as long as there's more than 3 scripts. Anyway, if a connection to obtain a script fails, the async allows the page to finish loading properly and correctly without the page hanging and waiting for the timeout, even if the script is placed at the bottom of the page.

@mathiasbynens

This comment has been minimized.

Show comment
Hide comment
@mathiasbynens

mathiasbynens Oct 10, 2013

Member

To avoid the IE <= 9 bug, @souders just suggested adding a <script> element in between that just does like var x = 42; or something. That might separate the execution. #fronteers13

Let’s run some tests!

Member

mathiasbynens commented Oct 10, 2013

To avoid the IE <= 9 bug, @souders just suggested adding a <script> element in between that just does like var x = 42; or something. That might separate the execution. #fronteers13

Let’s run some tests!

@brunoais

This comment has been minimized.

Show comment
Hide comment
@brunoais

brunoais Oct 10, 2013

Sounds like good news for me...
I'll be eager to wait for those results...

brunoais commented Oct 10, 2013

Sounds like good news for me...
I'll be eager to wait for those results...

@stevesouders

This comment has been minimized.

Show comment
Hide comment
@stevesouders

stevesouders Oct 10, 2013

I tried interleaving inline script blocks in Kyle's example but it did not fix the bug. Bummer.

http://stevesouders.com/tests/defer-ie-bug.php?fix=2

stevesouders commented Oct 10, 2013

I tried interleaving inline script blocks in Kyle's example but it did not fix the bug. Bummer.

http://stevesouders.com/tests/defer-ie-bug.php?fix=2

@DrDoorknob

This comment has been minimized.

Show comment
Hide comment
@DrDoorknob

DrDoorknob May 13, 2015

Because my organization supports IE9 and I've been asked to look into this, I figure I should throw in a comment even if it's a year ahead. I just put together a test of this in an IE9 VM, and couldn't reproduce the issue discussed; in fact I'm currently considering simply universally adding the "defer" attribute to our scripts.
What could potentially be causing the difference in behavior is that anyone working on this issue is trying to find very, very large javascript files that will always take longer to load than the small script below (which requires the large). JQuery is a popular example, and I'm now wondering if that happens to be doing something that causes glitchy behavior to just IE9; changing the DOM being a possible candidate. My test script was a 1.5MB custom pre-built bundle of the dojo framework, and many AMD module definitions; it's likely that script doesn't do anything to the page until DOMContentLoaded, so it seemed to be working okay.
To try to isolate whether frameworks like jquery are actually specifically relevant to the issue, another test could just involve a 2MB javascript file that just contains many random characters, does nothing, and then sets a flag on window.

DrDoorknob commented May 13, 2015

Because my organization supports IE9 and I've been asked to look into this, I figure I should throw in a comment even if it's a year ahead. I just put together a test of this in an IE9 VM, and couldn't reproduce the issue discussed; in fact I'm currently considering simply universally adding the "defer" attribute to our scripts.
What could potentially be causing the difference in behavior is that anyone working on this issue is trying to find very, very large javascript files that will always take longer to load than the small script below (which requires the large). JQuery is a popular example, and I'm now wondering if that happens to be doing something that causes glitchy behavior to just IE9; changing the DOM being a possible candidate. My test script was a 1.5MB custom pre-built bundle of the dojo framework, and many AMD module definitions; it's likely that script doesn't do anything to the page until DOMContentLoaded, so it seemed to be working okay.
To try to isolate whether frameworks like jquery are actually specifically relevant to the issue, another test could just involve a 2MB javascript file that just contains many random characters, does nothing, and then sets a flag on window.

@getify

This comment has been minimized.

Show comment
Hide comment
@getify

getify May 13, 2015

so it seemed to be working okay.

I wouldn't build my production strategy on such false assumptions, but YMMV. As far as I'm concerned, everything here about defer is still valid until IE9 stops being supported.

Even in IE10+, defer isn't fully reliable, as it will not work on inline scripts, which is unfortunately still a pretty common occurrence, even for just simple code like activating a social widget or GA tracking script, etc.

getify commented May 13, 2015

so it seemed to be working okay.

I wouldn't build my production strategy on such false assumptions, but YMMV. As far as I'm concerned, everything here about defer is still valid until IE9 stops being supported.

Even in IE10+, defer isn't fully reliable, as it will not work on inline scripts, which is unfortunately still a pretty common occurrence, even for just simple code like activating a social widget or GA tracking script, etc.

@DrDoorknob

This comment has been minimized.

Show comment
Hide comment
@DrDoorknob

DrDoorknob May 13, 2015

Didn't you just write this one earlier in the thread?
http://www.whatwg.org/specs/web-apps/current-work/multipage/scripting-1.html#attr-script-defer
"The defer and async attributes must not be specified if the src attribute is not present."

Any syncronicity between a defer+src script with an inline script (ie, something running a JQuery function) is coincidential, and undefined browser behavior.

I tend to think of inline scripts (as in, inline between pieces of content, not a single inline script in head that configures your libraries) as archaic deprecated behavior, like conditional HTML comments; , so I'm not surprised that having them in place breaks newer script-loading features.

DrDoorknob commented May 13, 2015

Didn't you just write this one earlier in the thread?
http://www.whatwg.org/specs/web-apps/current-work/multipage/scripting-1.html#attr-script-defer
"The defer and async attributes must not be specified if the src attribute is not present."

Any syncronicity between a defer+src script with an inline script (ie, something running a JQuery function) is coincidential, and undefined browser behavior.

I tend to think of inline scripts (as in, inline between pieces of content, not a single inline script in head that configures your libraries) as archaic deprecated behavior, like conditional HTML comments; , so I'm not surprised that having them in place breaks newer script-loading features.

vitorbaptista added a commit to opentrials/opentrials that referenced this issue Mar 25, 2016

[#40] Load JS dependencies using "defer" when not in >=IE9
IE <=9 doesn't guarantee the order of execution of script[defer]
(h5bp/lazyweb-requests#42)

vitorbaptista added a commit to opentrials/opentrials that referenced this issue Mar 25, 2016

[#40] Load JS dependencies using "defer" when not in >=IE9
IE <=9 doesn't guarantee the order of execution of script[defer]
(h5bp/lazyweb-requests#42)
@mamunbd24

This comment has been minimized.

Show comment
Hide comment
@mamunbd24

mamunbd24 May 17, 2017

Hello,

what is the difference between <script async=''async'' & <script async=''

mamunbd24 commented May 17, 2017

Hello,

what is the difference between <script async=''async'' & <script async=''

@DrDoorknob

This comment has been minimized.

Show comment
Hide comment
@DrDoorknob

DrDoorknob May 17, 2017

None, really...they're both syntactically accepted because some HTML-writing programs don't follow the Boolean nature of attributes, and because it's required for valid XML.

Attributes like "async" or "disabled" don't actually require an equals sign. It's valid to write <script async> or . Anything inside the equals is pretty much ignored. When checking for those Booleans, JS code usually checks "element.hasAttribute(x)" rather than "element.getAttribute()", so even if it registers as an empty string, it will turn out true.

DrDoorknob commented May 17, 2017

None, really...they're both syntactically accepted because some HTML-writing programs don't follow the Boolean nature of attributes, and because it's required for valid XML.

Attributes like "async" or "disabled" don't actually require an equals sign. It's valid to write <script async> or . Anything inside the equals is pretty much ignored. When checking for those Booleans, JS code usually checks "element.hasAttribute(x)" rather than "element.getAttribute()", so even if it registers as an empty string, it will turn out true.

@getify

This comment has been minimized.

Show comment
Hide comment
@getify

getify May 18, 2017

For posterity sake, let's not clutter up this thread with general support inquiries about web platform features. Please take this discussion to stackoverflow or some other forum where it's more appropriate, and remove the messages here.

getify commented May 18, 2017

For posterity sake, let's not clutter up this thread with general support inquiries about web platform features. Please take this discussion to stackoverflow or some other forum where it's more appropriate, and remove the messages here.

@vlakoff

This comment has been minimized.

Show comment
Hide comment
@vlakoff

vlakoff Mar 11, 2018

An user reported the suggested fix didn't work for him. If other users could confirm this, the original post should be edited so that people who land on it are not misled ;)

vlakoff commented Mar 11, 2018

An user reported the suggested fix didn't work for him. If other users could confirm this, the original post should be edited so that people who land on it are not misled ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment