动态加载JavaScript文件

时间:2020-03-05 18:41:48  来源:igfitidea点击:

如何可靠,动态地加载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文档。