Sameer Parwani
Sameer Parwani

Reputation: 309

domReady seems to fire late in RequireJS

In my application when I use require (from RequireJS), the domReady event fires on what seems to actually be the load event (when all images, scripts, etc are finished being loaded). Is this expected?

I have a bunch of code that uses document.ready (from jQuery) and it's not running properly because the domReady event is firing very late. I've written an example script that shows the problem I'm facing.

<html>
<head>
</head>
<body>
<script type="text/javascript" src="http://ratel.apache-extras.org.codespot.com/svn-history/r6/trunk/ratel/web/js/lib/require.js"></script>
<script>
    require.config({
        paths: {
            "jquery": '//ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min',
            "jquery.validate": '//ajax.microsoft.com/ajax/jquery.validate/1.7/jquery.validate.min'
        },
        shim: {
            "jquery.validate": ["jquery"]
        }
    });

    require(["jquery"], function (){
        $(document).ready(function(){
            console.log("the document is ready");
        });
    });
</script>
<img src="http://ie.microsoft.com/testdrive/HTML5/DOMContentLoaded/whidbey.jpg" width="300" height="300">
</body>
</html>

This HTML example loads a very large image. I would expect the code within $(document).ready would fire immediately after jQuery is loaded. But instead it fires only once the large image is done downloading.

What am I doing wrong here?

Upvotes: 2

Views: 1445

Answers (2)

Matthew Kime
Matthew Kime

Reputation: 754

https://github.com/jquery/jquery/blob/master/src/core/ready.js#L64

I recently ran into this problem on my own project and discovered that its a problem when jquery loads after DOMContentLoaded and before window.load. If you read through the jquery source listed above you'll see that jquery essentially has no way of knowing that DOMContentLoaded has fired which leaves your $.ready code waiting around for window.load.

This is a problem with loading jquery async, determining the state of document loading (internal to jquery), and loading large / slow files in the doc.

You have two choices - load jquery synchronously or stop using $.ready functionality.

Upvotes: 0

Paul Grime
Paul Grime

Reputation: 15104

Using the following jsfiddle test, which will attach listeners to window.DOMContentLoaded, window.load, $() and require["domReady!]:

<script>
// IE patch
if (Function.prototype.bind && window.console && typeof console.log == "object") {
    ["log","info","warn","error","assert","dir","clear","profile","profileEnd"].forEach(function (method) {
        console[method] = this.bind(console[method], console);
    }, Function.prototype.call);
}

startTime = +new Date();
require = {
    paths: {
        "domReady": "https://rawgithub.com/requirejs/domReady/2.0.1/domReady",
        "jquery": "http://code.jquery.com/jquery-1.10.2.min"
    },
    waitSeconds: 60
};
</script>
<script src="http://requirejs.org/docs/release/2.1.8/minified/require.js"></script>
<script>
function log() {
    var args = Array.prototype.slice.apply(arguments);
    var evt = args[0];
    var type = evt && evt.type;
    args = [+new Date() - startTime + "ms", type].concat(args);
    console.log.apply(console, args);
}

require(["jquery"], function($) {
    $(log("jquery.ready"));
});
require(["domReady!"], log);
window.addEventListener('load', log, false);
window.addEventListener('DOMContentLoaded', log, false);
</script>

<img id="theImg" src="http://ie.microsoft.com/testdrive/HTML5/DOMContentLoaded/whidbey.jpg" width="300" height="300">

<script>
var theImg = document.getElementById("theImg");
log("I am the trailing script and this is the image", theImg, theImg.id, theImg.parentNode, theImg.nextSibling);
</script>

I get these results (FF 23, Chrome 29, IE 9):

FF clean cache.

[22:08:47.477] "488ms" undefined "I am the trailing script and this is the image" [object HTMLImageElement] "theImg" [object HTMLBodyElement] [object Text]
[22:08:47.478] "489ms" "DOMContentLoaded" [object Event]
[22:08:47.838] "849ms" undefined "jquery.ready"
[22:09:00.770] "13781ms" "load" [object Event]
[22:09:00.773] "13783ms" undefined [object HTMLDocument]

FF primed cache.

[22:09:19.881] "3ms" undefined "I am the trailing script and this is the image" [object HTMLImageElement] "theImg" [object HTMLBodyElement] [object Text]
[22:09:19.881] "3ms" "DOMContentLoaded" [object Event]
[22:09:19.883] "5ms" "load" [object Event]
[22:09:19.908] "30ms" undefined "jquery.ready"
[22:09:20.176] "298ms" undefined [object HTMLDocument]

Chrome clean cache.

745ms undefined I am the trailing script and this is the image <img id=?"theImg" src=?"http:?/?/?ie.microsoft.com/?testdrive/?HTML5/?DOMContentLoaded/?whidbey.jpg" width=?"300" height=?"300">? theImg <body>?…?</body>? #text
751ms DOMContentLoaded Event {clipboardData: undefined, cancelBubble: false, returnValue: true, srcElement: document, defaultPrevented: false…}
2024ms undefined jquery.ready fiddle.jshell.net/fiddlegrimbo/QrUxr/17/show/:53
16105ms load Event {clipboardData: undefined, cancelBubble: false, returnValue: true, srcElement: document, defaultPrevented: false…}
16127ms undefined #document

Chrome primed cache.

503ms undefined I am the trailing script and this is the image <img id=?"theImg" src=?"http:?/?/?ie.microsoft.com/?testdrive/?HTML5/?DOMContentLoaded/?whidbey.jpg" width=?"300" height=?"300">? theImg <body>?…?</body>? #text
508ms DOMContentLoaded Event {clipboardData: undefined, cancelBubble: false, returnValue: true, srcElement: document, defaultPrevented: false…}
1211ms undefined jquery.ready fiddle.jshell.net/fiddlegrimbo/QrUxr/17/show/:53
14952ms load Event {clipboardData: undefined, cancelBubble: false, returnValue: true, srcElement: document, defaultPrevented: false…}
14953ms undefined #document

IE clean cache.

LOG: 363msundefinedI am the trailing script and this is the image[object HTMLImageElement]theImg[object HTMLBodyElement][object Text] 
LOG: 364msDOMContentLoaded[object Event] 
LOG: 1171msundefinedjquery.ready 
LOG: 13743msload[object Event] 
LOG: 13747msundefined[object Document] 

IE primed cache.

LOG: 6msundefinedI am the trailing script and this is the image[object HTMLImageElement]theImg[object HTMLBodyElement][object Text] 
LOG: 126msDOMContentLoaded[object Event] 
LOG: 128msload[object Event] 
LOG: 164msundefinedjquery.ready 
LOG: 416msundefined[object Document] 

So the order of callbacks is the same in FF, Chrome and IE when the cache is clean:

  • DOMContentLoaded
  • jquery.ready
  • window.load
  • domReady!

But IE's order changes when the cache is primed. FF and Chrome stay the same.

And both FF and IE seem to run far quicker second time around. Chrome seems to have roughly the same delays despite the cache being primed.

You will have to perform your own tests if necessary, as I only ran these a couple of times. So I am not saying this is consistent behaviour (although it could be).

Ignoring ready/load/$()/domReady! etc

You can of course ignore all these callbacks and simply add a script block at the end of your page inside your </body> tag. All the DOM elements above the script block should be available for certain manipulations. Again, you will have to test for your particular page/site.

Upvotes: 4

Related Questions