dynamically creating script: readyState never "complete"

14,351

Solution 1

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:

Solution 2

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();
        }
}
Share:
14,351

Related videos on Youtube

pepsi600
Author by

pepsi600

Updated on June 08, 2022

Comments

  • pepsi600
    pepsi600 about 2 years

    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
    pepsi600 almost 13 years
    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.
  • user278064
    user278064 almost 13 years
    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.
  • James Montagne
    James Montagne almost 13 years
    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.
  • pepsi600
    pepsi600 almost 13 years
    @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.
  • Semmel
    Semmel about 10 years
    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'); ...
  • Yuri Izgarshev
    Yuri Izgarshev almost 7 years
    @Semmel, thank you for your reply and notes - I've updated my answer with pure JS.