zl程序教程

您现在的位置是:首页 >  前端

当前栏目

多浏览器兼容的动态加载JavaScript与CSS

JavaScript浏览器CSS 动态 加载 兼容
2023-06-13 09:14:02 时间
在介绍ensure内部的实现之前,让我们先来看看其功能:

ensure({
html:"popup.html",
javascript:"popup.js",
css:"popup.css"
},function(){
Popup.show("helloworld");
}
);
在这段代码中,ensure首先会确保popup.html、popup.js、popup.css这3个文件的加载,如果都没加载过ensure就会动态加载它们;如果已经加载过了,ensure不会再次加载。在确保这3个文件都加载后,ensure会调用后面的匿名函数,也就是执行Popup.show("helloworld");。

接下来,就让我们看看ensure是如何动态加载JavaScript与CSS的。

加载JavaScript
在ensure当中,加载JavaScript分两种情况来执行,也就是Safari与非Safari这两种情况。

在IE、Firefox、Opera中加载JavaScript
在这三款浏览器中加载JavaScript,其实只需要创建一个script元素,把src指向要加载的URL,最后把script元素追加到head元素上,那就搞掂了。此项工作是在HttpLibrary.createScriptTag()中完成的。不过我们不仅仅要加载JavaScript,同时还需要知道它什么时候完成加载,这可以通过script元素的onload事件或onreadystatechange事件来实现。

在Safari中加载JavaScript
因为Safari2不支持onload或者onreadystatechange,所以只能手动通过XHR把URL读去过来,然后再手动eval这段代码,这就带来了一个限制──只能加载本域的JavaScript文件。在ensure当中,eval的工作是通过HttpLibrary.globalEval()来完成的。为了让JavaScript代码在全局(global)上下文中eval,ensure还是使用了创建script元素的方法,并将要eval的JavaScript置于其内,最后把script元素追加到head元素内。

细心的人肯定要问,为什么HttpLibrary.globalEval()要如此设计,而非直接window.eval或者eval.call。这是因为,window.eval和eval.call都无法在IE6中实现和script标签加载JavaScript代码一模一样的效果,这两种做法的eval在IE6下仍然不是在全局上下文中执行的。搜索一下你就会发现一些相关的讨论,例如jQuery就曾经使用window.execScript()来完成此项任务。不过最终大家都发现添加script元素才是最好的跨浏览器解决方案,所以现在的jQuery和ensure都是如此实现的了。

加载CSS
相对于加载JavaScript而言,加载CSS就简单多了,而且方法也是类似的:在head元素内直接加入link元素就可以了。这也正是loadCSS()所完成的工作。

实际上,ensure没有确保CSS完成加载后再执行下去。这估计是因为浏览器都能够在CSS加载完成后自动应用到页面上,因此OmarALZabir就认为CSS的加载顺序是无关紧要的,不过假如CSS加载速度实在太慢,其实还是会影响显示效果的。

在IE6中加载CSS
这次需要特别照顾的是IE6,而非Safari。IE6在往head元素添加link元素时,必须在window的上下文中完成,因此添加link的函数通过call调用切换了上下文。

总结
实际上动态加载JavaScript与CSS都并不难,在大多数情况下只需要向head元素追加对应的子元素就可以了,只有Safari2和IE6这两款古老的浏览器是需要特殊照顾的。
官方地址
ensure

复制代码代码如下:

/*
Script:Ensure.js

Ensurelibrary
    Atinyjavascriptlibrarythatprovidesahandyfunction"ensure"whichallowsyoutoload
    Javascript,HTML,CSSon-demandandthenexecuteyourcode.Ensureensuresthatrelevent
    JavascriptandHTMLsnippetsarealreadyinthebrowserDOMbeforeexecutingyourcode
    thatusesthem.

    Todownloadlastversionofthisscriptusethislink:<http://www.codeplex.com/ensure>

Version:
    1.0-Initialrelease

Compatibility:
    FireFox-Version2and3
    InternetExplorer-Version6and7
    Opera-9(probably8too)
    Safari-Version2and3
    Konqueror-Version3orgreater

Dependencies:
    <jQuery.js>
    <MicrosoftAJAX.js>
    <Prototype-1.6.0.js>

Credits:
    -GlobalJavascriptexecution-<http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html>

Author:
    OmarALZabir-http://msmvps.com/blogs/omar

License:
    >Copyright(C)2008OmarALZabir-http://msmvps.com/blogs/omar
    >    
    >Permissionisherebygranted,freeofcharge,
    >toanypersonobtainingacopyofthissoftwareandassociated
    >documentationfiles(the"Software"),
    >todealintheSoftwarewithoutrestriction,
    >includingwithoutlimitationtherightstouse,copy,modify,merge,
    >publish,distribute,sublicense,and/orsellcopiesoftheSoftware,
    >andtopermitpersonstowhomtheSoftwareisfurnishedtodoso,
    >subjecttothefollowingconditions:
    >
    >Theabovecopyrightnoticeandthispermissionnoticeshallbeincluded
    >inallcopiesorsubstantialportionsoftheSoftware.
    >
    >THESOFTWAREISPROVIDED"ASIS",WITHOUTWARRANTYOFANYKIND,EXPRESSORIMPLIED,
    >INCLUDINGBUTNOTLIMITEDTOTHEWARRANTIESOFMERCHANTABILITY,
    >FITNESSFORAPARTICULARPURPOSEANDNONINFRINGEMENT.
    >INNOEVENTSHALLTHEAUTHORSORCOPYRIGHTHOLDERSBELIABLEFORANYCLAIM,
    >DAMAGESOROTHERLIABILITY,WHETHERINANACTIONOFCONTRACT,TORTOROTHERWISE,
    >ARISINGFROM,OUTOFORINCONNECTIONWITHTHESOFTWARE
    >ORTHEUSEOROTHERDEALINGSINTHESOFTWARE.
*/

(function(){

window.ensure=function(data,callback,scope)
{
if(typeofjQuery=="undefined"&&typeofSys=="undefined"&&typeofPrototype=="undefined")
returnalert("jQuery,MicrosoftASP.NETAJAXorPrototypelibrarynotfound.Onemustbepresentforensuretowork");

//There"satestcriteriawhichwhenfalse,theassociatedcomponentsmustbeloaded.Butiftrue,
//noneedtoloadthecomponents
if(typeofdata.test!="undefined")
{
vartest=function(){returndata.test};

if(typeofdata.test=="string")
{
test=function()
{
//Ifthere"snosuchJavascriptvariableandthere"snosuchDOMelementwithIDthen
//thetestfails.Ifanyexists,thentestsucceeds
return!(eval("typeof"+data.test)=="undefined"
&&document.getElementById(data.test)==null);
}
}
elseif(typeofdata.test=="function")
{
test=data.test;
}

//Nowwehavetestprepared,timetoexecutethetestandseeifitreturnsnull,undefinedorfalseinany
//scenario.Ifitdoes,thenloadthespecifiedjavascript/html/css
if(test()===false||typeoftest()=="undefined"||test()==null)
newensureExecutor(data,callback,scope);
//Testsucceeded!Justfirethecallback
else
callback();
}
else
{
//Notestspecified.So,loadnecessaryjavascript/html/cssandexecutethecallback
newensureExecutor(data,callback,scope);
}
}

//ensureExecutoristhemainclassthatdoesthejobofensure.
window.ensureExecutor=function(data,callback,scope)
{
this.data=this.clone(data);
this.callback=(typeofscope=="undefined"||null==scope?callback:this.delegate(callback,scope));
this.loadStack=[];

if(data.js&&data.js.constructor!=Array)this.data.js=[data.js];
if(data.html&&data.html.constructor!=Array)this.data.html=[data.html];
if(data.css&&data.css.constructor!=Array)this.data.css=[data.css];

if(typeofdata.js=="undefined")this.data.js=[];
if(typeofdata.html=="undefined")this.data.html=[];
if(typeofdata.css=="undefined")this.data.css=[];

this.init();
this.load();
}

window.ensureExecutor.prototype={
init:function()
{
//FetchJavascriptusingFrameworkspecificlibrary
if(typeofjQuery!="undefined")
{
this.getJS=HttpLibrary.loadJavascript_jQuery;
this.httpGet=HttpLibrary.httpGet_jQuery;
}
elseif(typeofPrototype!="undefined")
{
this.getJS=HttpLibrary.loadJavascript_Prototype;
this.httpGet=HttpLibrary.httpGet_Prototype;
}
elseif(typeofSys!="undefined")
{
this.getJS=HttpLibrary.loadJavascript_MSAJAX;
this.httpGet=HttpLibrary.httpGet_MSAJAX;
}
else
{
throw"jQuery,PrototypeorMSAJAXframeworknotfound";
}
},
getJS:function(data)
{
//abstractfunctiontogetJavascriptandexecuteit
},
httpGet:function(url,callback)
{
//abstractfunctiontomakeHTTPGETcall
},
load:function()
{
this.loadJavascripts(this.delegate(function(){
this.loadCSS(this.delegate(function(){
this.loadHtml(this.delegate(function(){
this.callback()
}))
}))
}));
},
loadJavascripts:function(complete)
{
varscriptsToLoad=this.data.js.length;
if(0===scriptsToLoad)returncomplete();

this.forEach(this.data.js,function(href)
{
if(HttpLibrary.isUrlLoaded(href)||this.isTagLoaded("script","src",href))
{
scriptsToLoad--;
}
else
{
this.getJS({
url:href,
success:this.delegate(function(content)
{
scriptsToLoad--;
HttpLibrary.registerUrl(href);
}),
error:this.delegate(function(msg)
{
scriptsToLoad--;
if(typeofthis.data.error=="function")this.data.error(href,msg);
})
});
}
});

//waituntilalltheexternalscriptsaredownloaded
this.until({
test:function(){returnscriptsToLoad===0;},
delay:50,
callback:this.delegate(function()
{
complete();
})
});
},
loadCSS:function(complete)
{
if(0===this.data.css.length)returncomplete();

varhead=HttpLibrary.getHead();
this.forEach(this.data.css,function(href)
{
if(HttpLibrary.isUrlLoaded(href)||this.isTagLoaded("link","href",href))
{
//Donothing
}
else
{
varself=this;
try
{
(function(href,head)
{
varlink=document.createElement("link");
link.setAttribute("href",href);
link.setAttribute("rel","Stylesheet");
link.setAttribute("type","text/css");
head.appendChild(link);

HttpLibrary.registerUrl(href);
}).apply(window,[href,head]);
}
catch(e)
{
if(typeofself.data.error=="function")self.data.error(href,e.message);
}
}
});

complete();
},
loadHtml:function(complete)
{
varhtmlToDownload=this.data.html.length;
if(0===htmlToDownload)returncomplete();

this.forEach(this.data.html,function(href)
{
if(HttpLibrary.isUrlLoaded(href))
{
htmlToDownload--;
}
else
{
this.httpGet({
url:href,
success:this.delegate(function(content)
{
htmlToDownload--;
HttpLibrary.registerUrl(href);

varparent=(this.data.parent||document.body.appendChild(document.createElement("div")));
if(typeofparent=="string")parent=document.getElementById(parent);
parent.innerHTML=content;
}),
error:this.delegate(function(msg)
{
htmlToDownload--;
if(typeofthis.data.error=="function")this.data.error(href,msg);
})
});
}
});

//waituntilalltheexternalscriptsaredownloaded
this.until({
test:function(){returnhtmlToDownload===0;},
delay:50,
callback:this.delegate(function()
{
complete();
})
});
},
clone:function(obj)
{
varcloned={};
for(varpinobj)
{
varx=obj[p];

if(typeofx=="object")
{
if(x.constructor==Array)
{
vara=[];
for(vari=0;i<x.length;i++)a.push(x[i]);
cloned[p]=a;
}
else
{
cloned[p]=this.clone(x);
}
}
else
cloned[p]=x;
}

returncloned;
},
forEach:function(arr,callback)
{
varself=this;
for(vari=0;i<arr.length;i++)
callback.apply(self,[arr[i]]);
},
delegate:function(func,obj)
{
varcontext=obj||this;
returnfunction(){func.apply(context,arguments);}
},
until:function(o/*o={test:function(){...},delay:100,callback:function(){...}}*/)
{
if(o.test()===true)o.callback();
elsewindow.setTimeout(this.delegate(function(){this.until(o);}),o.delay||50);
},
isTagLoaded:function(tagName,attName,value)
{
//Createatemporarytagtoseewhatvaluebrowsereventually
//givestotheattributeafterdoingnecessaryencoding
vartag=document.createElement(tagName);
tag[attName]=value;
vartagFound=false;
vartags=document.getElementsByTagName(tagName);
this.forEach(tags,function(t)
{
if(tag[attName]===t[attName]){tagFound=true;returnfalse}
});
returntagFound;
}
}

varuserAgent=navigator.userAgent.toLowerCase();

//HttpLibraryisacrossbrowser,crossframeworklibrarytoperformcommonoperations
//likeHTTPGET,injectingscriptintoDOM,keepingtrackofloadedurletc.Itprovides
//implementationsforvariousframeworksincludingjQuery,MSAJAXorPrototype
varHttpLibrary={
browser:{
    version:(userAgent.match(/.+(?:rv|it|ra|ie)[\/:]([\d.]+)/)||[])[1],
    safari:/webkit/.test(userAgent),
    opera:/opera/.test(userAgent),
    msie:/msie/.test(userAgent)&&!/opera/.test(userAgent),
    mozilla:/mozilla/.test(userAgent)&&!/(compatible|webkit)/.test(userAgent)
},
loadedUrls:{},

isUrlLoaded:function(url)
{
returnHttpLibrary.loadedUrls[url]===true;
},
unregisterUrl:function(url)
{
HttpLibrary.loadedUrls[url]=false;
},
registerUrl:function(url)
{
HttpLibrary.loadedUrls[url]=true;
},

createScriptTag:function(url,success,error)
{
varscriptTag=document.createElement("script");
scriptTag.setAttribute("type","text/javascript");
scriptTag.setAttribute("src",url);
scriptTag.onload=scriptTag.onreadystatechange=function()
{
if((!this.readyState||
                    this.readyState=="loaded"||this.readyState=="complete")){
                success();
            }
        };
scriptTag.onerror=function()
{
error(data.url+"failedtoload");
};
    varhead=HttpLibrary.getHead();
head.appendChild(scriptTag);
},
getHead:function()
{
returndocument.getElementsByTagName("head")[0]||document.documentElement
},
globalEval:function(data)
{
varscript=document.createElement("script");
script.type="text/javascript";
        if(HttpLibrary.browser.msie)
            script.text=data;
        else
            script.appendChild(document.createTextNode(data));

varhead=HttpLibrary.getHead();
        head.appendChild(script);
        //head.removeChild(script);
},
loadJavascript_jQuery:function(data)
{
if(HttpLibrary.browser.safari)
{
returnjQuery.ajax({
            type:"GET",
            url:data.url,
            data:null,
            success:function(content)
            {
            HttpLibrary.globalEval(content);
            data.success();
            },
            error:function(xml,status,e)
{
if(xml&&xml.responseText)
data.error(xml.responseText);
else
data.error(url+"\n"+e.message);
},
            dataType:"html"
        });
}
else
{
HttpLibrary.createScriptTag(data.url,data.success,data.error);
}
},
loadJavascript_MSAJAX:function(data)
{
if(HttpLibrary.browser.safari)
{
varparams=
{
url:data.url,
success:function(content)
{
HttpLibrary.globalEval(content);
data.success(content);
},
error:data.error
};
HttpLibrary.httpGet_MSAJAX(params);
}
else
{
HttpLibrary.createScriptTag(data.url,data.success,data.error);
}
},
loadJavascript_Prototype:function(data)
{
if(HttpLibrary.browser.safari)
{
varparams=
{
url:data.url,
success:function(content)
{
HttpLibrary.globalEval(content);
data.success(content);
},
error:data.error
};
HttpLibrary.httpGet_Prototype(params);
}
else
{
HttpLibrary.createScriptTag(data.url,data.success,data.error);
}
},
httpGet_jQuery:function(data)
{
returnjQuery.ajax({
            type:"GET",
            url:data.url,
            data:null,
            success:data.success,
            error:function(xml,status,e)
{
if(xml&&xml.responseText)
data.error(xml.responseText);
else
data.error("Erroroccuredwhileloading:"+url+"\n"+e.message);
},
            dataType:data.type||"html"
        });
},
httpGet_MSAJAX:function(data)
{
var_wRequest=newSys.Net.WebRequest();
_wRequest.set_url(data.url);
_wRequest.set_httpVerb("GET");
_wRequest.add_completed(function(result)
{
varerrorMsg="Failedtoload:"+data.url;
if(result.get_timedOut()){
errorMsg="Timedout";
}
if(result.get_aborted()){
errorMsg="Aborted";
}

if(result.get_responseAvailable())data.success(result.get_responseData());
elsedata.error(errorMsg);
});

varexecutor=newSys.Net.XMLHttpExecutor();
_wRequest.set_executor(executor);
executor.executeRequest();
},
httpGet_Prototype:function(data)
{
newAjax.Request(data.url,{
method:"get",
evalJS:false,//Makesureprototypedoesnotautomaticallyevanscripts
onSuccess:function(transport,json)
{
data.success(transport.responseText||"");
},
onFailure:data.error
});
}
};

})();