ded / domready

lets you know when the dom is ready
MIT License
874 stars 129 forks source link

Domready breaks if called during "interactive" readyState #38

Closed paton closed 10 years ago

paton commented 10 years ago

1) Load the domready library when the document readyState is "interactive" 2) Immediately call domready(callback) when the document is still in "interactive" state 3) Callback is never executed

Domready assumes that the DOMContentLoaded event will be fired when the document transitions from interactive to complete.

However, DOMContentLoaded won't fire again after the document is in interactive mode. This results in the domready callback never executing (loaded is false, and DOMContentLoaded never fires).

Looks like the hack in @theodoreb's pull request would fix this (at least for Chrome, not sure if it would work 100% in IE).

gagle commented 10 years ago

Add a test case, please. This works for me in IE7+ (not tested on IE<7) and all the other browsers. Anyway, why are you calling domReady when it's interactive?:

var addEvent = function (el, type, cb){
    if (el.addEventListener){
        el.addEventListener (type, cb, false);
    }else if (el.attachEvent){
        el.attachEvent ("on" + type, cb);
    }
};

addEvent (document, "readystatechange", function (){
    console.log (document.readyState);
    if (document.readyState === "interactive"){
        domReady (function (){
            console.log ("dom ready");
        });
    }
});

Result:

interactive
dom ready
complete
paton commented 10 years ago

@gagle Here's a test case that breaks on latest Chrome.

For the bug to present itself, the domready library must be loaded when the page is in interactive state.

var interval = setInterval(function() {
  if (document.readyState !== 'interactive') return;
  clearInterval(interval);

  var domready = (function () {
    var fns = [], listener
      , doc = document
      , domContentLoaded = 'DOMContentLoaded'
      , loaded = /^loaded|^c/.test(doc.readyState)

    if (!loaded)
    doc.addEventListener(domContentLoaded, listener = function () {
      doc.removeEventListener(domContentLoaded, listener)
      loaded = 1
      while (listener = fns.shift()) listener()
    })

    return function (fn) {
      loaded ? fn() : fns.push(fn)
    }
  })();

  var currentState = document.readyState;
  console.log('Calling domready during: ' + currentState);
  domready(function(){ console.log('domready loaded from: ' + currentState); });

}, 1);
gagle commented 10 years ago

Have you tried this other lib with the same test case?

paton commented 10 years ago

I haven't -- why?

For my specific use case, I fixed this issue with https://github.com/theodoreb/domready/commit/93db9f24676c4edbb7fa03caa279d6b4301602d9

gagle commented 10 years ago

It seems to handle iframes.

latentflip commented 10 years ago

I just ran into this as well.

Turns out if the file is loaded in an async script tag, it'll get loaded in the interactive state, here's an example:

http://jsbin.com/yalijehanaku/1/

it pulls in this script as an async script tag, which is just the domready code after it.

domready(function () {
  console.log('Loaded successfully');
});

You'll see in the jsbin that the log doesn't occur, but if you remove the async attribute from the script tag it does.

ded commented 10 years ago

fixed in #32