【问题标题】:script onload/onerror with IE(for lazy loading) problems带有 IE 的脚本 onload/onerror(用于延迟加载)问题
【发布时间】:2010-08-14 15:08:22
【问题描述】:

我正在重建我的惰性加载器模块以接受异步请求,但我有一个大问题:
Internet Explorer 不支持 script.onload/onerror!!!

旧脚本对使用 ajax 同步调用读取的目标脚本源进行全局评估, 它工作得很好,它是跨浏览器的,我可以让它异步编辑 1 个变量,但调试起来非常棘手(所有源代码都在一行中执行,浏览器没有提供太多关于错误的信息,将代码除以使用正则表达式是不可能的,因为 js 有无限深度的块,而正则表达式根本不擅长这个)。

这是我用来创建脚本的代码(经典):

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>;

它不能在 IE 上运行,因为它不支持脚本的 onload 和 onerror;
下面的代码是一个修复程序,但仅在脚本不是异步的情况下才有效

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>;

            };

我可以每隔 X 毫秒对其进行一次测试,直到脚本被加载,但这是一个丑陋的解决方案,我想避免它。

编辑:这是我尝试每 X 毫秒检查一次脚本是否已加载的代码,它并没有那么糟糕,而且它比 ajax 工作得更好;问题是我不知道是否脚本加载成功或错误(onload 或 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);

我尝试使用 addEventListener/attachEvent 函数,但它似乎不起作用(即使使用网络上的 addEvent 函数)

总结选项似乎是:

  • 使用 AJAX 和全局 eval 加载脚本(调试地狱)
  • 仅在 IE 中使用 AJAX 和全局评估(可能是一个解决方案,我不使用 IE)
  • 仅当脚本包含错误时才使用 AJAX 和全局评估(我需要检查计时问题,因为即使调用是异步的,我的代码也会“模拟”同步代码)
  • 每隔 X 次测试 script.onreadystatechange(仅在 IE 上),直到它被加载(丑陋!!!)
  • 使用window.onload : 避免,它需要对所有页面收费,我需要在只启动一个脚本时调用它(详见endpage)
  • 在每个脚本的源代码上添加一个代码(避免像最后页所说的那样)
  • 修复 IE 的 script.onload(使用 addEventListener/attachEvent?!?)


请注意:
我不想使用window.onload,因为它仅在加载所有页面时触发,我需要在仅加载目标脚本时触发它(我的延迟加载脚本要复杂得多,所以请不要问为什么);
我不想使用任何第三方库(如 jquery、prototype 等),
我什至不想编辑目标脚本源(例如在使用 JSPON 或添加脚本以提醒脚本已加载时)。

希望不要太多! 谢谢。

【问题讨论】:

标签: javascript internet-explorer asynchronous lazy-loading


【解决方案1】:

这是一种解决方案: 如果是 IE,我将简单地使用异步 ajax 调用加载文本,然后将 script.text 设置为加载的数据。 IE似乎锁定了onload和onerror(出于安全原因?)而不是script.text(其他一些浏览器可能不允许它用于安全原因以防止像iframes这样的XSS攻击),我不知道为什么微软不能简单地尊重标准,我只是讨厌 ie 和“技巧”来解决他们的设计问题。

    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---

这在大多数浏览器上都很好用(IE/chrome/firfox 已经测试过了),我测试了加载 3 个文件:

  • file1 加载时间为 4s
  • file2 出现 500 错误
  • file3 加载时间为 1

并且它们在所有浏览器中总共加载了 40XX 毫秒(浏览器需要一些额外的时间来调用 onload/onerror 脚本),我还可以(使用我的惰性加载器脚本)模拟同步加载执行仅在队列中的所有文件都加载完毕后编码。

如果您知道更好的方法或者您知道此实现中可能出现的错误,请回复! 谢谢!

【讨论】:

  • 我会适应你的解决方案,除了它有一个缺点,这对我来说是个大问题:你对跨域 AJAX 调用以及本地 (file:// ) AJAX 调用,:( 从而使系统不可靠。
【解决方案2】:
script.onreadystatechange = function() {
    if (script.readyState == 'loaded')//ERROR LOADING
        <my_onerror_code>;
    else if(script.readyState == 'complete')//loaded
        <my_onload_code>;

};

不得不说,不管有没有设置async,用readyState = 'loaded'检查错误是不够的。其实loaded会在加载错误脚本或第一次加载正确脚本的情况下触发。

您可以在src 中附加随机查询字符串来禁用缓存,然后检查readyState

【讨论】:

    【解决方案3】:

    忽略下面的垃圾。 nextSibling 的事情是在调试器中的结果,在现实世界中并不能真正重现。相反,我不得不建议查看 www.requirejs.org

    它提供了一种与 js 中的 include 或 import 语句差不多的方法。

    这是一个完全傻瓜式的解决方案,我将关注这个线程,让 cmets 了解它为什么有效,但这里是我如何在没有计时器的情况下解决这个问题的。

    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;

    就像我说的我不知道为什么 nextSibling 在尝试 404 时为空,但如果 js url 正确,nextSibling 就有一个值。

    【讨论】:

    • 经过进一步测试,这似乎只在调试时有效 - 在 if(!complete... 语句中断之后。
    • 我敢打赌 nextSibling 是空的,因为 404'ing 脚本元素是你的测试中附加到 head 的最后一件事。
    【解决方案4】:

    试试这个:https://stackoverflow.com/a/18840568/2785975。这里描述了带有 onerror 事件的 IE7-8 的问题并显示了决策。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-25
      • 2015-06-17
      • 2021-04-20
      • 2021-04-23
      • 2012-08-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多