11

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.

pepsi600
  • 145
  • 1
  • 2
  • 7
  • I'm not sure what you want to do, but I suggest looking into `jQuery#getScript` for an idea of how to dynamically load scripts. Most importantly, using an AJAX pattern will let you test for HTTP errors as you seem to have in mind. That cannot -- afaik -- be accomplished using DOM `Events` on the `script` tag. – FK82 Aug 04 '11 at 20:35
  • Thanks for the input, FK82. I just want to be 100% sure that the script is loaded. As I said, in FF/Chrome, onload and onerror works perfectly. But under IE, even if a script url is invalid (or maybe timed out), readyState still returns loaded. The reason I want to use pure Javascript is becasue I want to make sure jQuery is loaded correctly from a CDN first, and then use jQuery to do stuff. And yes, I too find that this should go the AJAX way. (haven't tried tho) – pepsi600 Aug 04 '11 at 22:26
  • Please have a look at my answer. You will be able to catch various HTTP errors using the AJAX/XHR (`XMLHttpRequest`) pattern. Also, I believe this would be more stable in terms of cross-browser compatibility. – FK82 Aug 05 '11 at 08:29

3 Answers3

12

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();
        }
}
Yuri Izgarshev
  • 121
  • 1
  • 4
  • This is by far the **best solution for** script injection in **IE8** with success and error handlers. It is far better then loading the script via AJAX because by script injection the **AJAX CORS limits do not apply**. Great find with the `node.children` trick! Btw. `var node = createNode( 'script',` ... is of course in plain vanilla JavaScript `var node = document.createElement('script');` ... – Semmel Jun 05 '14 at 22:29
  • I'm not sure that the node.onerror is working in IE9. – f1lt3r Apr 14 '16 at 21:32
  • @Semmel, thank you for your reply and notes - I've updated my answer with pure JS. – Yuri Izgarshev Aug 08 '17 at 18:47
5

As per your comment, here's a schematic of how to dynamically add a script tag using XHR (XMLHttpRequest):

var handleRequest = function( ) { //!! set up the handleRequest callback

     if(this.status != undefined) {

         /* do something with the status code here */

     }

     if(this.readyState == 4) {

          var script = document.createElement("script") ;
              script.setAttribute("type","text/javascript") ;
          var text = document.createTextNode(this.responseText) ;
              script.appendChild(text) ;

          var head = document.getElementsByTagName("head")[0] ;
              head.insertBefore(script,head.firstChild) ;

     }

} ;

var request ; //!! supposing you have a way to get a working XHR Object

//.. set the XHR Object

request.open("GET",url,true) ;
request.overrideMimeType("text/javascript") ;
request.onreadystatechange = handleRequest ;
request.send(null) ;

Please keep in mind that this is only to give you an idea of what I mean. A working example would have to be way more elaborate judging from the jQuery source code.


Links:

FK82
  • 4,907
  • 4
  • 29
  • 42
  • your solution rocks, but to be 100% it should be enhanced with CORS handling so it can also load scripts outside of your domain, otherwise the scripts you can load are limited to the same origin policy :) – rupps Mar 18 '13 at 21:58
  • Thanks. ( : You're right in as far as CORS implementations are supported cross-browser. The fallback for cross domain script access is using the SCRIPT tag however. – FK82 May 21 '13 at 08:33
-1

Detecting Load Completion

[..] One web page suggested setting up some event handlers that will be called when the load is complete. We do that by adding the following lines to the previous code:

var head= document.getElementsByTagName('head')[0];
var script= document.createElement('script');
script.type= 'text/javascript';
script.onreadystatechange= function () {
  if (this.readyState == 'complete') helper();
}
script.onload= helper;
script.src= 'helper.js';
head.appendChild(script);

Here we set up two different event handlers on the newly created script tag. Depending on the browser, one or the other of these two handlers is supposed to be called when the script has finished loading. The onreadystatechange handler works on IE only. The onload handler works on Gecko browsers and Opera.

The "this.readyState == 'complete'" test doesn't actually entirely work. The readyState theoretically goes through a series of states:

  • 0 uninitialized
  • 1 loading
  • 2 loaded
  • 3 interactive
  • 4 complete

But in fact, states may be skipped. In my experience with IE 7, you get either a loaded event or a completed event, but not both.

It may have something to do with whether you are loading from cache or not but there seem to be other factors that influence which events you get. Sometimes I get loading or interactive events too, and sometimes I don't. It's possible the test should be "this.readyState == 'loaded' || this.readyState == 'complete'", but that risks triggering twice.

http://unixpapa.com/js/dyna.html

UPDATE

Please, can you sobstitute your script.onreadystatechange callback with this:

newjs.onreadystatechange = function () {
   if (newjs.readyState === 'loaded' || newjs.readyState === 'complete') {
      newjs.onreadystatechange = null;
      alert('script is complete or loaded.");
   }

};

An other thing i've noticed is that you're doing string comparison with == operator. This can be misleading. Use === instead.

user278064
  • 9,982
  • 1
  • 33
  • 46
  • Come on........did you just copy and paste? You got exactly the same code as mine (I mean the structure), and this is what I'm talking about...the "readyState" is never "complete"!!! And I'm trying to figure it out. – pepsi600 Aug 04 '11 at 18:40
  • It seems that there is no way to know if loaded or complete will be fired. Look javascript pattern "Loading on Demand". The author wrote this: • In IE you subscribe to the readystatechange event and look for a readyState “loaded” or “complete.” All other browsers will ignore this. – user278064 Aug 04 '11 at 18:54
  • 1
    He's saying that load IS fired, but it fires even when the script is invalid and fails (404, etc.). He wants to know how to detect that. – James Montagne Aug 04 '11 at 19:59
  • @your update: well, that's not the point. == and === in this case are the same. readyState returns a string, and loaded/complete are surrounded with quotes, so, we're not comparing types. The problem is that IE just can't do the work with DOM event. I'm switching to AJAX... Thanks anyway. – pepsi600 Aug 04 '11 at 23:15