script[defer] doesn't work in IE<=9 #42
Description
(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...
- 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.
- 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.
defer
used on a script tag referencing an external resource behaved differently thandefer
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.defer
on a script tag written out by adocument.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:
- http://stevesouders.com/hpws/js-defer.php
- http://stevesouders.com/efws/couple-script-defer.php
- http://hacks.mozilla.org/2009/06/defer/
- @aaronpeters @Schepp
Activity
paulirish commentedon Aug 10, 2011
kyle added....
paulirish commentedon Aug 10, 2011
Spec: http://www.whatwg.org/specs/web-apps/current-work/multipage/scripting-1.html#attr-script-defer
getify commentedon Aug 10, 2011
To give more context to the above list of
defer
issues: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 usedefer
when you're dealing with markup, andasync=false
when you're dynamically creating script elements. The latter would make you assume you could/should useasync
in markup, but that's not going to work because order is not preserved -- unless of course you happen to not care about order.DOMContentLoaded
event (aka, "DOM-ready") until after all thedefer
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).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 usedefer
, if those inline blocks are relying on ordering (almost always they are).document.write()
, but some unfortunate souls have to deal with that reality (aka, "nightmare").jdalton commentedon Aug 10, 2011
I made a test which seems to confirm the following:
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 commentedon Aug 10, 2011
Isn't blocking the visual rendering only supposed to occur with non-
defer
ed scripts? What would be the advantage ofdefer
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 adefer
-attribute yet.In regards to
DOMContentLoaded
event being triggered too early, maybe the following manualDOMContentLoaded
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-ojdalton commentedon Aug 10, 2011
@Schepp
I think that might cause problems with some handlers as it's generally assumed
DOMContentLoaded
is only fired once.mathiasbynens commentedon Aug 11, 2011
FWIW:
Source
aaronpeters commentedon 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 commentedon 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 commentedon 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 aDOMContentLoaded
discrepancy between browsers we would not gain anything performance-wise.defer
really 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-defer
all scripts that you come across as they might contain adocument.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 commentedon 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 withdefer
?mathiasbynens commentedon 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 commentedon Aug 11, 2011
If the
defer
attribute were defined that it should push the scripts to start executing immediately after it fired theDOMContentLoaded
event (like it does in FF), thendefer
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 commentedon 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.
http://stevesouders.com/cuzillion/?c0=hj1hfft2_0_f&c1=hj1hfft2_0_f&c2=hb0hfff0_0_f&t=1313075137650
robflaherty commentedon 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. Addingdefer
changes this and causesDOMContentLoaded
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.
57 remaining items
vlakoff commentedon 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 ;)
Rename Create front-end-performance-checklist-2019-pdf-pages-center t…
Update front-end-performance-checklist-2019-pdf-pages-center
Create front-end-performance-checklist-2019-pdf-pages two
stale commentedon Mar 1, 2019
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
stale commentedon Mar 18, 2019
This issue has been automatically closed because it has not had recent activity. Thank you for your contributions.