动态加载JavaScript文件
如何可靠,动态地加载JavaScript文件?这将用于实现一个模块或者组件,该模块或者组件在"初始化"时将根据需要动态加载所有需要的JavaScript库脚本。
使用该组件的客户端不需要加载仅实现"主"组件脚本文件的所有库脚本文件(并将" <script>"标记手动插入其网页)。
主流JavaScript库如何做到这一点(原型,jQuery等)?这些工具是否将多个JavaScript文件合并到脚本文件的单个可再发行"构建"版本中?还是他们对附属的"库"脚本进行了动态加载?
这个问题的补充:动态加载的JavaScript文件加载后,是否可以处理事件?原型具有用于整个文档范围事件的" document.observe"。例子:
document.observe("dom:loaded", function() { // initially hide all containers for tab content $$('div.tabcontent').invoke('hide'); });
脚本元素有哪些可用事件?
解决方案
回答
这是我发现的一些示例代码...有人有更好的方法吗?
function include(url) { var s = document.createElement("script"); s.setAttribute("type", "text/javascript"); s.setAttribute("src", url); var nodes = document.getElementsByTagName("*"); var node = nodes[nodes.length -1].parentNode; node.appendChild(s); }
回答
所有主要的JavaScript库(例如jscript,prototype,YUI)都支持加载脚本文件。例如,在YUI中,加载核心后,我们可以执行以下操作来加载日历控件
var loader = new YAHOO.util.YUILoader({ require: ['calendar'], // what components? base: '../../build/',//where do they live? //filter: "DEBUG", //use debug versions (or apply some //some other filter? //loadOptional: true, //load all optional dependencies? //onSuccess is the function that YUI Loader //should call when all components are successfully loaded. onSuccess: function() { //Once the YUI Calendar Control and dependencies are on //the page, we'll verify that our target container is //available in the DOM and then instantiate a default //calendar into it: YAHOO.util.Event.onAvailable("calendar_container", function() { var myCal = new YAHOO.widget.Calendar("mycal_id", "calendar_container"); myCal.render(); }) }, // should a failure occur, the onFailure function will be executed onFailure: function(o) { alert("error: " + YAHOO.lang.dump(o)); } }); // Calculate the dependency and insert the required scripts and css resources // into the document loader.insert();
回答
我所做的基本上与我们使用Adam时所做的相同,但是稍作修改以确保我将添加到head标签上来完成工作。我只是创建了一个包含函数(下面的代码)来处理脚本和CSS文件。
此功能还检查以确保脚本或者CSS文件尚未动态加载。它不检查手工编码的值,并且可能有更好的方法来做到这一点,但它确实达到了目的。
function include( url, type ){ // First make sure it hasn't been loaded by something else. if( Array.contains( includedFile, url ) ) return; // Determine the MIME-type var jsExpr = new RegExp( "js$", "i" ); var cssExpr = new RegExp( "css$", "i" ); if( type == null ) if( jsExpr.test( url ) ) type = 'text/javascript'; else if( cssExpr.test( url ) ) type = 'text/css'; // Create the appropriate element. var tag = null; switch( type ){ case 'text/javascript' : tag = document.createElement( 'script' ); tag.type = type; tag.src = url; break; case 'text/css' : tag = document.createElement( 'link' ); tag.rel = 'stylesheet'; tag.type = type; tag.href = url; break; } // Insert it to the <head> and the array to ensure it is not // loaded again. document.getElementsByTagName("head")[0].appendChild( tag ); Array.add( includedFile, url ); }
回答
does anyone have a better way?
我认为仅将脚本添加到正文中,然后将其添加到页面上的最后一个节点会更容易。这个怎么样:
function include(url) { var s = document.createElement("script"); s.setAttribute("type", "text/javascript"); s.setAttribute("src", url); document.body.appendChild(s); }
回答
我们在工作中使用的技术是使用AJAX请求来请求javascript文件,然后使用eval()返回。如果我们使用的是原型库,则它们在其Ajax.Request调用中支持此功能。
回答
我最近在jQuery中使用了一个简单得多的版本:
<script src="scripts/jquery.js"></script> <script> var js = ["scripts/jquery.dimensions.js", "scripts/shadedborder.js", "scripts/jqmodal.js", "scripts/main.js"]; var $head = $("head"); for (var i = 0; i < js.length; i++) { $head.append("<script src=\"" + js[i] + "\"></scr" + "ipt>"); } </script>
在我在IE6 / 7,Firefox,Safari,Opera中测试过的每种浏览器中,它的效果都很好。
更新:无jQuery版本:
<script> var js = ["scripts/jquery.dimensions.js", "scripts/shadedborder.js", "scripts/jqmodal.js", "scripts/main.js"]; for (var i = 0, l = js.length; i < l; i++) { document.getElementsByTagName("head")[0].innerHTML += ("<script src=\"" + js[i] + "\"></scr" + "ipt>"); } </script>
回答
我使用了我在网上找到的另一种解决方案...该解决方案处于creativecommons之下,它会在调用函数之前检查是否包含了源代码...
我们可以在这里找到文件:include.js
/** include - including .js files from JS - [email protected] - 2005-02-09 ** Code licensed under Creative Commons Attribution-ShareAlike License ** http://creativecommons.org/licenses/by-sa/2.0/ **/ var hIncludes = null; function include(sURI) { if (document.getElementsByTagName) { if (!hIncludes) { hIncludes = {}; var cScripts = document.getElementsByTagName("script"); for (var i=0,len=cScripts.length; i < len; i++) if (cScripts[i].src) hIncludes[cScripts[i].src] = true; } if (!hIncludes[sURI]) { var oNew = document.createElement("script"); oNew.type = "text/javascript"; oNew.src = sURI; hIncludes[sURI]=true; document.getElementsByTagName("head")[0].appendChild(oNew); } } }
回答
我们可以编写动态脚本标签(使用原型):
new Element("script", {src: "myBigCodeLibrary.js", type: "text/javascript"});
这里的问题是我们不知道外部脚本文件何时完全加载。
我们经常希望我们的依赖代码在下一行,并且喜欢编写如下代码:
if (iNeedSomeMore) { Script.load("myBigCodeLibrary.js"); // includes code for myFancyMethod(); myFancyMethod(); // cool, no need for callbacks! }
有一种聪明的方法可以注入脚本依赖项,而无需回调。我们只需要通过同步AJAX请求提取脚本并在全局级别评估脚本。
如果使用原型,则Script.load方法如下所示:
var Script = { _loadedScripts: [], include: function(script) { // include script only once if (this._loadedScripts.include(script)) { return false; } // request file synchronous var code = new Ajax.Request(script, { asynchronous: false, method: "GET", evalJS: false, evalJSON: false }).transport.responseText; // eval code on global level if (Prototype.Browser.IE) { window.execScript(code); } else if (Prototype.Browser.WebKit) { $$("head").first().insert(Object.extend( new Element("script", { type: "text/javascript" }), { text: code } )); } else { window.eval(code); } // remember included script this._loadedScripts.push(script); } };
回答
刚刚发现了YUI 3的一项重要功能(撰写本文时已在预览版中提供)。我们可以轻松地将依赖项插入到YUI库和"外部"模块(我们正在寻找的)中,而无需太多代码:YUI Loader。
它还会在加载外部模块后回答有关正在调用的函数的第二个问题。
例子:
YUI({ modules: { 'simple': { fullpath: "http://example.com/public/js/simple.js" }, 'complicated': { fullpath: "http://example.com/public/js/complicated.js" requires: ['simple'] // <-- dependency to 'simple' module } }, timeout: 10000 }).use('complicated', function(Y, result) { // called as soon as 'complicated' is loaded if (!result.success) { // loading failed, or timeout handleError(result.msg); } else { // call a function that needs 'complicated' doSomethingComplicated(...); } });
对我来说工作完美,并且具有管理依赖项的优势。有关YUI 2日历的示例,请参考YUI文档。