dynamically creating script: readyState never "complete"

pepsi600 picture pepsi600 · Aug 4, 2011 · Viewed 14.2k times · Source

I'm trying to do something AFTER a script is completely loaded. (IE8)

Script I use for testing: http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js
and the invalid one: http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.minaaaaaaaa.js

The code...

var script = create the element and append to head...

// this works fine with FF/Chrome/...
script.onload = function() {alert('script loading complete');}
script.onerror = function() {alert('error loading script');}

// and for IE
script.onreadystatechange = function() {
    // this will alert 1.loading 2.loaded
    alert(this.readyState);

    // this never works
    if(this.readyState == 'complete') {alert('script loading complete');}

    // this works with either a valid or INVALID url
    else if(this.readyState == 'loaded') {alert('script loaded');}
};

In my case, the "complete" never shows, the "loaded" shows even if a url is invalid. So there's no way to tell if a script is CORRECTLY loaded UNDER IE.

Am I doing something wrong? How come I never get the complete state?

UPDATE

OK, I just read some articles and it seems that readystate is not a reliable way to detect script loading.

So is there another way to do so? without jQuery, but pure Javascript.

Answer

Yuri Izgarshev picture Yuri Izgarshev · Sep 17, 2013

I've found out a trick how to make script node become 'complete' in IE7 and IE8. And also how to detect when error really happens when loading script (node.onerror working only in IE9+). The hack is in

  1. NOT inserting the newly created element into DOM (at once).
  2. Calling node.children property and checking node.onreadystate property right after it. If the property changes to 'complete' - you have loaded script, it it changes to 'loading' - it's for sure script loading error.

Try it!

var node = document.createElement('script');
node.src = 'some-[un]existing-url.js';
node.type = 'text/javscript';

node.onreadystatechange = (function(node) { 
    return function () {
        if (/loaded|complete/.test(node.readyState)) {
            _finish();
        }
    };
})(node);

var _finish=function() {

        if (node.readyState == 'complete') {
            // insert node only after completing the request, though this is not necessary
            var head = document.head;
            head || (head = document.getElementsByTagName('head')[0]);
            head.appendChild(node);
            // call success function
            _success();
            return;
        }

        var firstState = node.readyState;

        // hack: calling 'children' property changes node's readyState from 'loaded' to complete
        // (if script was loaded normally) or to 'loading' - if error detected
        node.children;

        // error detected
        if (firstState == 'loaded' && node.readyState == 'loading') {
            // custom error code
            _error();
        }
}