<script defer> fires after $(function) in Chrome and FX
See original GitHub issueIn Chrome, FX and IE >=10 <script defer>
should cause scripts to be background loaded, but then executed in order before DOMContentLoaded
fires.
However, when lots of scripts are loaded with defer
applied the jQuery event fires before all the scripts have run.
You can test this with:
file 1.js:
window.test = "fail init";
// test result when jQuery ready fires
$(function() {$('#resultJQ').html("jQuery result: " + window.test);});
// test result when document is ready
document.addEventListener("DOMContentLoaded", function() {$('#resultDOM').html("DOM result: " + window.test);});
file 2-9.js:
window.test = "fail " + n;
file 10.js:
window.test = "pass"
page.html:
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="jquery.js" defer></script>
<script type="text/javascript" src="1.js" defer></script>
...
<script type="text/javascript" src="n.js" defer></script>
...
<script type="text/javascript" src="10.js" defer></script>
</head>
<body>
<div id="resultJQ">waiting...</div>
<div id="resultDOM">waiting...</div>
<body>
</html>
Both tests pass when files load fast enough, but sufficient delays (>50ms each) in the *.js
files cause the jQuery test to fail in latest Chrome and FX. It doesn’t fail consistently, but the jQuery ready always fires before the document one.
Here is a jsBin that reproduces it on first load, although this might depend on your network and subsequent loads won’t show the bug. Try throttling your connection and CTRL+F5 if it doesn’t happen on first load.
This bug is extant in the latest 2.x and 3.x but doesn’t seem to occur in 1.x
This appears to be due to this check:
// Catch cases where $(document).ready() is called
// after the browser event has already occurred.
// Support: IE <=9 - 10 only
// Older IE sometimes signals "interactive" too soon
if ( document.readyState === "complete" ||
( document.readyState !== "loading" && !document.documentElement.doScroll ) ) {
// Handle it asynchronously to allow scripts the opportunity to delay ready
window.setTimeout( jQuery.ready );
}
When debugging it appears that document.readyState === 'interactive'
and !document.documentElement.doScroll
is true, so jQuery.ready
fires with the minimum delay (4ms). Breaking on window.setTimeout
halts execution while some of the numbered js files are still pending
in the Network tab.
I don’t know whether this is a recent change in behavior, but Chrome and Firefox appear to report document.readyState === 'interactive'
as soon as the body is present, but before deferred scripts are evaluated.
This isn’t a problem in IE < 10 as it doesn’t support defer
(it sort of does, but fires scripts out of order and so would fail this test anyway).
Issue Analytics
- State:
- Created 7 years ago
- Reactions:9
- Comments:26 (8 by maintainers)
TL/DR is “don’t load jQuery with defer” ? I struggled hours with a bug before I ended up here 😕 (and yes removing defer solves it)
@shmdhussain The ticket has all that information. Open, assigned to timmywil, scheduled for 4.0 since it is a breaking change for people depending on the current behavior.