script onload/onerror with IE(for lazy loading) problems
I'm rebuilding my lazy loader module to accept asyncronus request but i have a BIG problem:
internet explorer don't support script.onload/onerror!!!The old script did globally eval the target script source read with an ajax sync call, it works very well,it's cross browser and i can make it async editing 1 variable but it's very tricky to debug(ALL the source code is executed in one single line and the browser gives not to much infos about the errors,dividing the code by line with a regexp is IMPOSSIBLE because js have blocks with infinite depth and regexp are simply not good at this).
This is the code that i use to create the script(a classic):
var script = document.createElement('script');
script.type = 'text/javascript';
script.src =name;
script.name =name;
script.async = true;
script.onload=<my_onload_code>;
script.onerror=<my_onerror_code>;
it won't work on IE because it doesen't support onload and onerror with the script;
the code below is a fix but works only if the script isn't asyncif(script.onreadystatechange!==undefined)//only IE T_T
script.onreadystatechange = function() {
if (script.readyState == 'loaded')//ERROR LOADING
<my_onerror_code>;
else
if(script.readyState == 'complete')//loaded
<my_onload_code>;
};
i can test it every X milliseconds until the script is loaded but it's an ugly solution and i want to avoid it.
EDIT: this is the code i tryed to check every X ms if the script is loaded, it's not that bad and it works better than ajax;the problem is that i can't know if the script is loaded with success or with error(onload or onerror).
var script = document.createElement('script');
script.type = 'text/javascript';
script.src =name;
script.name =name;
script.async = true;
script.onload=function(){Lazy_loader.onload(this);};
script.onerror=function(){Lazy_loader.onerror(this);};
if(script.onreadystatechange!==undefined){//ie fix T_T
script.timer=setInterval(function(){
if (script.readyState == 'loaded' || script.readyState == 'complete')}//ERROR LOADING
if(LOADED???)//loaded
Lazy_loader.onload(script);
else
Lazy_loader.onerror(script);
clearInterval(script.timer);
}
},100);
}
document.getElementsByTagName('head')[0].appendChild(script);
i tryed to use addEventListener/attachEvent functions but it didn't seem to work(even using addEvent functions from the web)
summarizing the options seems to be:
- Use AJAX and global eval to load the script(debugging hell)
- Use AJAX and global eval only with IE(may be a solution,i don't use IE)
- Use AJAX and global eval only if the script contains errors(i need to check timings problems because with my code i "simulate" syncronous code even if the call is async)
- Test script.onreadystatechange (only on IE) every X time until it's loaded(UGLY!!!)
- Use window.onload : AVOID,it n开发者_StackOverflow社区eed to charge ALL the page,i need to call it when ONLY one script is launched(see details on endpage)
- Add a code on the source of each script (AVOID like said on endpage)
- fix the script.onload for IE(using addEventListener/attachEvent?!?)
PLEASE PAY ATTENTION: i don't want to use window.onload because it's fired only when ALL the page is loaded,i need to fire it when only the target script is loaded(my lazy loading script is a lot more complex so please don't ask why); i DO NOT WANT to use ANY third party library(like jquery,prototype,etc.), i don't even want to edit the target script source(like when using JSPON or adding a script to alert that the script is loaded).
Hope that's not too much! Thanks.
This is one solution: if it's IE i'll simply load the text with an async ajax call and then set the script.text to the loaded data. IE seems to lock onload and onerror(for security reasons?)and not script.text(some other browsers may not allow it for security resons to prevent XSS attacks like on iframes),i don't know why microsoft can't simply respect standards,i simply hate ie and "tricks" to fix theyr's desing problems.
var script = document.createElement('script');
script.type = 'text/javascript';
//---start IE fix---
if(window.ActiveXObject){//ie fix T_T
var xmlhttp=null;
try {
xmlhttp = new XMLHttpRequest();
}catch(e){
try{
xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
}catch(e){
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
}
xmlhttp.onreadystatechange = function() {
try{
if(this.done!==undefined)
return;
if(this.status >= 200 && this.status < 300){//loaded
this.done=true;
script.text=this.responseText;
document.getElementsByTagName('head')[0].appendChild(script);
Lazy_loader.onload({name:name});
}
if(this.status >= 400){
this.done=true;
Lazy_loader.onerror({name:name});
}
}catch(e){}
};
xmlhttp.open('get',name,true);
xmlhttp.send(null);
}
else{//browser that support script.onload/onerror
script.src =name;
script.name =name;
script.async = true;
script.onload=function(){Lazy_loader.onload(this);};
script.onerror=function(){Lazy_loader.onerror(this);};
document.getElementsByTagName('head')[0].appendChild(script);
}
//---end IE fix---
this works well on most browsers(IE/chrome/firfox tested for now)and i tested loading 3 files:
- file1 with load time of 4s
- file2 with a 500 error
- file3 with 1s of loading
and they're loaded in a total of 40XX ms in all browsers(some extra time is taken by the browser to call the onload/onerror script),i can also(with my lazy loader script)simulate a sync loading executing code only after all files in queue are loaded.
If you know a better way or if you know errors that may occur in this implementation please reply! Thanks!
script.onreadystatechange = function() {
if (script.readyState == 'loaded')//ERROR LOADING
<my_onerror_code>;
else if(script.readyState == 'complete')//loaded
<my_onload_code>;
};
I have to say that regardless whether async
is set or not, using readyState = 'loaded'
to check error is not enough. In fact, loaded
will be triggered in situations loading error script or the first time to load correct script.
You can append src
with random query string to disable the cache, then check the readyState
.
ignore the junk below. the nextSibling thing was a result of being in the debugger and wasn't really reproducible in the real world. instead, i would have to recommend checking out www.requirejs.org
it provides a method that is about as close to an include or import statement as you could find in js.
this is a totally goofball solution, and i will be following this thread for comments about why this works, but here goes how i fixed this without a timer.
var url = load_obj.url;
var callback = load_obj.callback;
var head = document.getElementsByTagName('head')[0];
var appendage;
var complete = false;
appendage = document.createElement('script');
appendage.type = 'text/javascript';
appendage.onload = appendage.onreadystatechange = function() {
if (!complete && (!this.readyState || this.readyState === 'complete' || (this.readyState === 'loaded' && this.nextSibling != null))) {
console.log('loaded via all');
complete = true;
if (callback)
callback();
//remove listeners
appendage.onload = appendage.onreadystatechange = null;
} else if (this.readyState === 'loaded' && this.nextSibling == null) {
console.log('error via ie');
}
appendage.onerror = function() {
console.log('error via everything else');
}
appendage.src = url;
like i said i don't know WHY nextSibling is null on a 404'd attempt, but if the js url was correct, nextSibling had a value.
Try this one: https://stackoverflow.com/a/18840568/2785975. Here the problem for IE7-8 with onerror event is described and decision is shown.
精彩评论