dynamically creating script: readyState never "complete"
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.jsThe code...
var script = create the element and append to head...
// this works fine with FF/Chrome/...
script.onload = function() {a开发者_开发百科lert('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.
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
- NOT inserting the newly created element into DOM (at once).
- 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();
}
}
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:
W3 documentation for XMLHttpRequest
MDN documentation for XMLHttpRequest
MSDN documentation for XMLHttpRequest
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.
精彩评论