39

I have replaced window.addEventListener('DOMContentLoaded', function() {}); with jQuery's $(document).bind('ready', function() {});, because first one failed to work on IE < 9 and I did not wanted to play with .attachEvent() for that dummy browser, if I could have this nicely covered by jQuery itself.

Shortly after replacement, I noticed that DOMContentLoaded event was always fired around 0-2 miliseconds after page load / refresh (at least this is what was logged by my logging script), while .ready() always requires at least 15-20 miliseconds, after page refresh, to be fired (again - as logged by script).

I'm asking purely for feeding my curiosity, why there is such "significant" delay? Of course, there is no problem for me, that jQuery is firing that event later. It is just, that because I want to know ALL the answers (and rule the world! :]), I can't sleep with that! :]

EDIT: in .ready() function doc some user (Nick (of Nexxar)) points out that: "jQuery simulates the non existing "DOMContentLoaded" event on IE, but the used mechanism fires much later than the event used on other browsers". Maybe this is the same, I'm asking for?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
trejder
  • 17,148
  • 27
  • 124
  • 216

4 Answers4

26

Assuming browser that supports the event:

  1. The real event can support any document. jQuery will only use the document it was loaded in, no matter what you pass to it.
  2. jQuery will fire the event asynchronously even if the event has already happened. Attaching 'DOMContentLoaded' event will do nothing if the event has already happened.

There is no delay in these browsers, see http://jsfiddle.net/rqTAX/3/ (the offsets logged are in milliseconds).

For browsers that don't support the event, jQuery's will obviously work for them as well. It will use a hacky mechanism that is not the same as the real DOMContentLoaded and will not necessarily fire as soon as the real DOMContentLoaded would:

// The DOM ready check for Internet Explorer
function doScrollCheck() {
    if ( jQuery.isReady ) {
        return;
    }

    try {
        // If IE is used, use the trick by Diego Perini
        // http://javascript.nwbox.com/IEContentLoaded/
        document.documentElement.doScroll("left");
    } catch(e) {
        setTimeout( doScrollCheck, 1 );
        return;
    }

    // and execute any waiting functions
    jQuery.ready();
}
Esailija
  • 138,174
  • 23
  • 272
  • 326
  • If I'm not getting you wrong, your answer only explains, why there could be a delay (change of time, when event is fired) in browsers that DOESN'T support `DOMContentLoaded` (like IE < 9). But I tested the thing in a browser that surely supports it (Chrome 20 and Firefox 13) and got delays of around 15-20 seconds in both browsers. – trejder Jul 17 '12 at 14:06
  • @trejder sorry I am not seeing anything like that whatsoever. See http://jsfiddle.net/rqTAX/3/. The differences are like 5-20 milliseconds. In chrome and firefox – Esailija Jul 17 '12 at 14:11
  • No need to sorry! :] It was purely my curiosity. Maybe I missed something. I think, we can close this topic, as for me. Thanks! – trejder Jul 17 '12 at 14:20
  • @trejder well you could at least accept my answer for providing the 2 differences at top that most people don't know about ;) Btw, you should use `$(document).ready` instead of `.bind`, they are very different. – Esailija Jul 17 '12 at 14:22
  • Thanks for reminding me, as I was `sure` I did accepted your answer! :] Sorry! BTW: Why `$(document).bind('ready', function() {});` and `$(document).ready(function() {});` are so different? – trejder Jul 17 '12 at 14:39
  • @trejder the `.bind` version won't get jQuery as first argument to the function and also won't work after the DOMContentLoaded has happened. So you lose these advantages. It is also very uncommon, this is the first time I see it. – Esailija Jul 17 '12 at 14:42
  • Well... In, for example, `.load()` [method description](http://api.jquery.com/load-event/) there is an information: "_This method is a shortcut for `.bind('load', handler)`._". So, I assumed that the same relation goes here. That `.ready(handler)` is just a shortcut for `.bind('ready', handler)`. – trejder Jul 18 '12 at 07:34
5

jQuery simulates this event by binding to the document's readystatechange event, which is the standard way of simulating DOMContentLoaded in oldIE.

According to the jQuery source, that event fires "late" but before window.onload. However, I can't find when that event fires exactly. DOMContentLoaded fires when the DOM is built and ready for scripting, so readystatechange fires after that; perhaps it waits for layout rendering or styling something like that, or the event is triggered later in the rendering/layout process?

Regardless, it will likely fire after DOMContentLoaded, likely due to when IE decides to update the document's readyState to "complete."

(If anyone has a definite answer, post a comment and I'll update this answer; I'd love to know when exactly it fires myself, and I can't find that answer in any documentation or on any sites I would expect like Quirksmode.)

ajm
  • 19,795
  • 3
  • 32
  • 37
  • OK, but similar like above. You're mostly talking about IE. And the very same delays happens in both newest Chrome and newest Firefox. – trejder Jul 17 '12 at 14:07
  • DOMContentLoaded happens AFTER scripts (regular and `defer`ed) are run (except `async` ones) – Simon_Weaver Feb 22 '17 at 00:19
2

Another reason for the 'ready' appearing to fire later (in practice) is that there may be many events hooked up to it. If any of these are long running synchronous operations then the ready event will come much later. For instance I use knockout.js and it can take 500ms to initialize the page.


However using your minimal test page that isn't the case of course. I tried running the following :

 console.log(window.performance.timing.domContentLoadedEventEnd -
             window.performance.timing.domContentLoadedEventStart);

This gives about 6ms even for your simple super page


Also I took your code and ran it through Chrome's performance tools and discovered a few interesting things:

enter image description here

  • BTW: The vertical blue bar is DOMCONTENTLOADED and the green is 'first paint'
  • You can see even just a super simple function call on the DOMCONTENTLOADED event can take 5ms (and this is on an i7). Remember it needs to be parsed and things need initializing.
  • The callbacks are shown in the cyan color (anonymous), the first is from the DOMCONTENTLOADED event and the second is the 'ready' callback.
  • You will notice 'Timer Fired' (i.e. setTimeout). So it is not instantaneous at by any means.

Looking at the jQuery source code you see they actually set a timer for the callbacks:

// Handle it asynchronously to allow scripts the opportunity to delay ready
window.setTimeout( jQuery.ready );

I'm not exactly sure what they mean here (any idea) - but it explains the behavior. I could see it being helpful to avoid race conditions, and blocking the UI but 'delaying ready' is unclear to me.

Simon_Weaver
  • 140,023
  • 84
  • 646
  • 689
  • The `setTimeout()` is a workaround for IE9-10 firing off the `DOMContentLoaded` event before a listener is added for it. The code checks to see if `document.readyState` has already completed, then adds a `setTimeout()` to push the `jQuery.ready` callback to the event queue, giving time for the other queued async callbacks a chance to execute before `jQuery.ready` is executed. – Daniel T. Feb 24 '17 at 08:06
  • but I'm using Chrome! the timing is probably negligible, but I'm trying to fire up a SignalR connection and need that done as soon as possible – Simon_Weaver Feb 25 '17 at 00:46
  • Chrome properly handles it, it doesn't fire off the `DOMContentLoaded` event until it reaches the end of the `

    ` tag. IE9-10 does not, so it needs a workaround. The jQuery code checks for this in an if statement. It falls back to the proper behavior if the `document.readyState` is not ready.

    – Daniel T. Feb 25 '17 at 00:53
  • yes it *works* but there is an unnecessary delay due to the timer. maybe there is some kind of race condition going on but I found it faster to put my SignalR initialization code in my own DOMContentLoaded as I only need to support modern browsers – Simon_Weaver Feb 25 '17 at 00:56
  • That's likely due to the parsing of jQuery itself, which is 84KB. – Daniel T. Feb 25 '17 at 01:10
  • Also, isn't it likely that using the devtools to monitor this slows it down as well? – Kyle Baker Apr 07 '17 at 15:55
  • @KyleBaker the Dev tools here was more to show that something happens in between. The window performance timing values wouldn't be affected by having tools turned on. – Simon_Weaver Apr 08 '17 at 00:33
1

Does anybody still use jQuery except me? ;)

As of jQuery 3.0, only the $( handler ) syntax is recommended,
(which is equivalent to $( document ).ready( handler )).

From API docs: .ready()

SergO
  • 2,703
  • 1
  • 30
  • 23
  • This makes more to difficult to find where it is. I am handling a big Razor project with lots of occurrences of `$( document ).ready(handler)).` and if someone would change to this, every other dev on the team would complain... – sergiol Mar 16 '23 at 13:11