完美异步加载javascript并回调函数
加载js方法很,但判断是否成功方法却少有。
异步加载js文件,大部分都是生成script标签插入文档,比如我们看看seajs3.0的源码:
var supportOnload = "onload" in node if (supportOnload) { node.onload = onload node.onerror = function() { emit("error", { uri: url, node: node }) onload(true) } } else { node.onreadystatechange = function() { if (/loaded|complete/.test(node.readyState)) { onload() } } }
从源可以看出只要不支持onload事件的浏览器压根就没有判断onerrer事件,也就是说无法知道文件加载失败情况。
其实主要是IE8及以下浏览器对js无onload事件支持,但支持onreadystatechange事件,而readyState主要有:
0 "uninitialized" 未初始化
1 "loading" 载入中
2 "loaded" 载入完成已经接收到全部响应内容
3 "interactive" 正在解析响应内容
4 "completed" 响应内容解析完成
而且特别是在IE下,往往因先赋值src再插入节点还是先插入节点再赋值src生成readyState返回loaded、completed不一至,所以需要两者一起判断。而且更可气的是不管文件是否成功加载仍会按顺序影响这些readyState,也就是说靠onreadystatechange事件无法分辨是否成功加载js文件了……
突然发现一个点子,把js以vbscript文档加载进入文档,必然会引发window.onerror事件:
var script = document.createElement("script"); if ("onload" in script) { script.onload = function () {}; script.onerror = function () {}; } else { script.language = "vbscript"; var _timeOut = setTimeout(function () { alert("失败"); }, 2000); window.onerror = function () { clearInterval(_timeOut); script = document.createElement("script"); script.onreadystatechange = function () { if (/loaded|complete/i.test(this.readyState)) {alert("成功")} }; script.type = "text/javascript"; script.src = jsUrl; document.getElementsByTagName("head")[0].appendChild(script); return false; }; } script.src = jsUrl; head.appendChild(script);
终于解决关键问题,可是这定时触发失败的时间又是一个问题,到底应该定多长时间为失败呢?经过测试一般js在100KB以内,2秒内完全可以载完。可是的可是假如文件较大或者网速卡了这如何是好?
我们必须等文件载完方可判断是否失败,而且不管文件是否加载成功必须会触发readyState变化,这……经过反复修改终于完美出版此方法:
function loadScript(jsUrl, callBack) { /* * 加载单个script并回调函数 * jsUrl : js地址字符串 * callBack:function(state,node) 回调函数(可选)state = ok|no;node为script节点 * 原创:348736477@qq.com * 作用:加载本地或者网络多个文本文件(除vbscript)大小不限并判断成功与否 * 如有问题或者改进请通知,谢谢 * */ var _doc = document, script = _doc.createElement("script"), head = _doc.head || _doc.getElementsByTagName("head")[0] || _doc.documentElement; 'function' != typeof callBack && (callBack = function(){});//确保有回调函数 if ("onload" in script) { //非IE和IE9及以上支持onload、onerror事件 script.type = "text/javascript"; script.onload = function () { callBack.call(null,"ok",this); }; script.onerror = function () { callBack.call(null,"no",this); }; } else { var baseElement = head.getElementsByTagName("base")[0],_err = 0; script.language = "vbscript"; //IE8及以下先把js当vbscript文件引入,如果触发window.onerror说明js有效。假如您的脚本也使用了window.onerror请使用监听方法替换以免冲突 window.onerror = function(){ _err = 1; return true;//忽略错误 }; script.attachEvent("onreadystatechange",function(){ //vbscript成功载入将会先触发window.onerror if (/loaded|complete/i.test(script.readyState)) { window.onerror = null;//清理监听事件 if(_err){ script.parentNode.removeChild(script);//移除vbscript节点 script = document.createElement("script");//再生成script节点并插入文档 script.attachEvent("onreadystatechange",function(){ if (/loaded|complete/i.test(script.readyState)) { callBack.call(null, "ok", script); } }); script.type = "text/javascript"; script.src = jsUrl; //IE6下script必须插入到base前,以防引起bug baseElement ? head.insertBefore(script, baseElement) : head.appendChild(script); }else{ callBack.call(null,"no",script); } } }); } script.src = jsUrl;//必须先赋值后生成,否则影响readyState值 head.appendChild(script); } //调用 loadScript("http://www.scscms.com/1/test.js?a=" + Math.random(), function (state,node) { alert("ok" == state ? "成功":"失败"); } );
最后希望您能测试有问题请通知我,谢谢!
最后的最后我告诉你:它也是有一个小bug呢!假如在IE8以下你加载一个vbscript伪装成的js文件那也会误报哟!
=========================假如需要加载多个js文件情况下请使用以下函数===============================
function loadScripts(jsArray,callBack) { "use strict"; /* * 按序加载多个script并回调函数 * jsArray : 单个js地址字符串或者是js文件地址数组 * callBack:function(state,node) 回调函数(可选)state = ok|no;node为script节点 * 原创:348736477@qq.com * 作用:加载本地或者网络多个文本文件(除vbscript)大小不限并判断成功与否 * 如有问题或者改进请通知,谢谢 * */ var _doc = document, script, head = _doc.head || _doc.getElementsByTagName("head")[0] || _doc.documentElement, baseElement = head.getElementsByTagName("base")[0]; function loadJs() { if(0 == jsArray.length){ callBack.call(null,"ok",script); return; } var jsUrl = jsArray.shift(),_err = 0; script = _doc.createElement("script"); if ("onload" in script) { script.type = "text/javascript"; script.onload = function () { loadJs(); }; script.onerror = function () { callBack.call(null,"no",this); }; } else { script.language = "vbscript"; //IE8及以下先把js当vbscript文件引入,如果触发window.onerror说明js有效 //假如您的脚本也使用了window.onerror请使用监听方法替换以免冲突 window.onerror = function(){ _err = 1; return true;//忽略错误 }; script.attachEvent("onreadystatechange",function(){ //vbscript成功载入将会先触发window.onerror if (/loaded|complete/i.test(script.readyState)) { window.onerror = null;//清理监听事件 if(_err){ script.parentNode.removeChild(script);//移除vbscript节点 script = document.createElement("script");//再生成script节点并插入文档 script.attachEvent("onreadystatechange",function(){ if (/loaded|complete/i.test(script.readyState)) { loadJs(); } }); script.type = "text/javascript"; script.src = jsUrl; //IE6下script必须插入到base前,以防引起bug baseElement ? head.insertBefore(script, baseElement) : head.appendChild(script); }else{ callBack.call(null,"no",script); } } }); } script.src = jsUrl; head.appendChild(script); } '[object Array]' != Object.prototype.toString.call(jsArray) && (jsArray = [jsArray]);//确保参数为数组 'function' != typeof callBack && (callBack = function(){});//确保有回调函数 loadJs(); } //调用 loadScripts(["http://www.scscms.com/1/test.js","http://www.scscms.com/1/jquery-easyui.js","http://www.scscms.com/1/jquerd.js"], function (state,node) { if("no"==state){ alert(node.src+"文件加载失败!"); }else{ alert("成功!"); } } );
更新日期:2015-10-12 去除定时器机制
关键词: javascript,异步加载 编辑时间: 2015-10-12 16:45:00
0
高兴0
支持0
搞笑1
不解0
谎言0
枪稿0
震惊0
无奈0
无聊0
反对0
愤怒
- 暂无评论
网友评论