外部脚本加载方式中最常用的就是用js语句动态生成script标签并指定src值。这种方式基本上可以兼容所有浏览器,但真正的问题是出在如何指定callback上面。
我们还是来看看ie,在我的测试当中,IE7以下都不支持script标签的onload事件,取而代之的是onreadystatechange事件,并且此事件有个特别的地方,即使ie下,onreadystatechange 的调用 并不是紧紧跟在脚本执行或者失败之后, 而是有一点点延迟,尤其是当有这个请求有缓存的时候。而有可能这一点点延迟的时候就有另一个脚本回调进来了,因此会出现意想不到的问题,这里的解决方案是使用script标签的event 和 for属性。
event 指定一个事件,for属性指定一个id,这样当触发这个事件的时候,script标签就会加载,而最重要的这个事件会发生在script加载完毕之后。所以就有了一下解决方案。
var scriptTag = document.createElement("script");
scriptTag.event = "onclick";
scriptTag.src = "js/temp1.js";
scriptTag.id = scriptTag.htmlFor = "sc1";
document.body.appendChild(scriptTag);
scriptTag.onreadystatechange = function() {
if ( /loaded|complete/.test( scriptTag.readyState ) ) {
try {
scriptTag.onclick();
} catch( e ) {}
alert();
}
};
但这里是仅仅针对了ie做出了对策,同样的,我们可以继而针对所有浏览器做出对策,代码如下:
var addScript = function(setting, callback){
var src = setting["src"],
//默认60s超时
timeout = setting["timeout"] || 60000,
parentNode = setting["parentNode"] || document.body,
//是否加载完成
done = false,
handle,
//判断是否是老的ie
isOldIE = !!document.attachEvent && !(window.opera && toString.call(window.opera) == '[object Opera]');
var getRandId = function(){
return 'as_' + parseInt(Math.random()*100);
}
var scriptTag = document.createElement("script");
scriptTag.src = src;
if( isOldIE ) {
scriptTag.event = "onclick";
scriptTag.id = scriptTag.htmlFor = "sc1";
}
parentNode.appendChild(scriptTag);
handle = window.setTimeout(function(){
done = true;
throw new Error("timeout : " + src);
scriptTag.parentNode.removeChild(scriptTag);
},timeout);
scriptTag.onload = scriptTag.onreadystatechange = function() {
if ( isOldIE && /loaded|complete/.test(scriptTag.readyState) ) {
try {
scriptTag.onclick();
} catch( e ) {}
}
clearTimeout(handle);
scriptTag.onload = null;
scriptTag.onreadystatechange = null;
//执行回调
done = true;
callback();
};
};
熟悉yepnope的同学可能对上述加载方式并不陌生,
yepnope正式通过以上的方式来动态的加载js文件。根据以上的理解,也让我们自己实现一个yepnope变得不再困难。
以上内容均为本人原创,转载请著名出处。同时也欢迎各位批评指正。