Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 async

if(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 need 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.

like image 948
Plokko Avatar asked Aug 14 '10 15:08

Plokko


2 Answers

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!

like image 70
Plokko Avatar answered Nov 06 '22 06:11

Plokko


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.

like image 31
nozer0 Avatar answered Nov 06 '22 06:11

nozer0