动态包含JavaScript和等待

mel*_*oon 5 javascript ajax

我想要做的是从Javascript中动态包含一个或多个js文件.我知道有很多技巧,但我正在寻找遵循这些规则的解决方案:

1)不使用任何JS框架或库(jQuery,Prototype等).

2)脚本应该暂停执行,直到外部js文件被浏览器完全加载和解析.

#2背后的理性是外部js文件将包含在包含脚本后需要立即使用的函数定义.大多数情况下,在我开始调用这些函数之前,浏览器没有时间解析js文件.

我不介意使用回调来知道脚本何时完成加载,但是:

1)我不知道会提前动态包含多少脚本,所以我不想也不能写一堆嵌套的回调.我只需要知道它们何时完成装载.

2)我的措辞是,如果浏览器缓存了JavaScript,尝试使用某种"加载"事件来触发回调可能不起作用.

编辑我应该从一开始就提到这是一个插件系统,但我想保持我的问题/答案足够通用,以便对其他人有所帮助.

用户可以定义他们想要在数组中加载哪些插件.

plugins = [ 'FooPlugin', 'BarPlugin' ];
Run Code Online (Sandbox Code Playgroud)

然后我将遍历数组,并为每个插件加载js脚本:

for(var i = 0; i < plugins.length; i++) {
    loadScript('plugins/' + plugins[i] + '.js');
}
Run Code Online (Sandbox Code Playgroud)

每个插件都将自己推送到loaded_plugins数组(这是FooPlugin.js的一个例子)

load_plugins.push({
    name: 'FooPlugin',
    // Other plugin methods and properties here
});
Run Code Online (Sandbox Code Playgroud)

For*_*rdi 3

跨浏览器工作,从 IE6 开始。

document.loadScript = function (src, callback) {
    function script(src, onload) {
        var scriptTag = document.createElement('script');
        if (onload) scriptTag.onload = onload;
        scriptTag.src = src;
        scriptTag.type = 'text/javascript';
        return scriptTag;
    }
    function outer(tag) { 
        var d = document.createElement('div');
        d.appendChild(tag);
        return d.innerHTML;
    }
    if (!(src instanceof Array)) src = [src];
    var i, scr, 
        callbackId = 'dynCall_'+Math.floor(Math.random()*89999+10000);
        counter = src.length, 
        call = function () { if (--counter == 0) callback(); };
    if (!document.body) {
        window[callbackId] = function () {
            delete window[callbackId];
            if (callback instanceof Function) callback();
        };
        for (i=0; i<src.length; i++) document.write(outer(script(src[i])));
        document.write('<scr'+'ipt type="text/javascript">'+'window.'+callbackId+'();'+'</scr'+'ipt>');
        return;
    }
    for (i=0; i<src.length; i++) document.body.appendChild(script(src[i], call));
};
Run Code Online (Sandbox Code Playgroud)

缩小/混淆:

(function(){document.loadScript=function(src,callback){function script(src,onload){var scriptTag=document.createElement('script');if(onload)scriptTag.onload=onload;scriptTag.src=src;scriptTag.type='text/javascript';return scriptTag}function outer(tag){var d=document.createElement('div');d.appendChild(tag);return d.innerHTML}if(!(src instanceof Array))src=[src];var i,scr,callbackId='dynCall_'+Math.floor(Math.random()*89999+10000);counter=src.length,call=function(){if(--counter==0)callback()};if(!document.body){window[callbackId]=function(){delete window[callbackId];if(callback instanceof Function)callback()};for(i=0;i<src.length;i++)document.write(outer(script(src[i])));document.write('<scr'+'ipt type="text/javascript">'+'window.'+callbackId+'();'+'</scr'+'ipt>');return}for(i=0;i<src.length;i++)document.body.appendChild(script(src[i],call))};document.loadScript.toString=function(){return'function loadScript() { [obfuscated] }'}})();
Run Code Online (Sandbox Code Playgroud)

分支的快速解释:

如果调用 loadScript 的脚本标签位于文档头或正文中,则 document.body 将是未定义的,因为 DOM 尚未存在。因此,我们不能使用附加标签的标准方法,而必须使用 doc write。这样做的优点是我们编写的标签将按一定的顺序发生,但缺点是我们必须有一个全局范围的回调函数。

同时,如果我们有 document.body,我们就可以做正确的事情(-ish - 当没有库来帮助做正确的事情时,我们会做出牺牲,因此 .onload() 。不过,这并不像我们那样即将在脚本标记上抛出大量事件,因此可能没问题。)缺点(也许?)是脚本全部异步加载,因此我们需要在加载时运行倒计时。