代码之家  ›  专栏  ›  技术社区  ›  mellowsoon

动态包含javascript并等待

  •  5
  • mellowsoon  · 技术社区  · 15 年前

    我要做的是在JavaScript中动态地包含一个或多个JS文件。我知道有很多方法可以解决这个问题,但是我正在寻找一个遵循这些规则的解决方案:

    1)不使用任何JS框架或库(jquery、原型等)。

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

    Rational Behind 2是外部JS文件,它将包含需要在包含脚本之后立即使用的函数定义。大多数时候,在我开始调用这些函数之前,浏览器没有时间解析JS文件。

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

    1)我不知道有多少脚本会被动态包含,所以我不想也不能写一堆嵌套回调。我只需要知道它们什么时候装好。

    2)我的措词是,如果浏览器缓存了javascript,尝试使用某种“加载”事件来触发回调可能不起作用。

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

    用户将定义要在数组中加载的插件。

    plugins = [ 'FooPlugin', 'BarPlugin' ];
    

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

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

    每个插件将自己推到加载的插件数组上(这是fooplugin.js的一个示例)

    load_plugins.push({
        name: 'FooPlugin',
        // Other plugin methods and properties here
    });
    
    3 回复  |  直到 15 年前
        1
  •  3
  •   Fordi    15 年前

    跨浏览器工作,从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));
    };
    

    缩小/模糊:

    (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] }'}})();
    

    分支机构的快速说明:

    如果调用loadscript的脚本标记在文档头或文档体中,则document.body将未定义,因为dom尚不在其中。因此,我们不能使用附加标签的标准方法,我们必须使用DocWrite。这样做的好处是,我们编写的标记将按一定的顺序发生,但缺点是我们必须具有全局范围回调函数。

    同时,如果我们有document.body,那么我们就可以很好地完成它(-ish-当周围没有库来帮助我们完成它时,我们会做出牺牲,因此就有了.onload()。不过,这并不是说我们要在脚本标签上抛出很多事件,所以它可能是正常的。)缺点(可能是?)所有脚本都是异步加载的,因此我们需要在加载时进行倒计时。

        2
  •  2
  •   Paul    15 年前

    我知道最简单(也是最可靠)的解决方案是简单地使用document.write在文档中插入一个新的脚本标记(当然,也可以根据需要插入任意多个脚本标记):

    <script type="text/javascript">document.write('<' + 'script src="/assets/js/file.js" type="text/javascript"><\/script>');</script>
    

    浏览器将(afaik)按照脚本标记在文档中指定的顺序分析和执行脚本,也可以通过这种方式添加。因此,如果将要执行的其余代码放在一个新的、单独的脚本标记中,那么应该是好的。

    编辑 :这里有(很多) more elaborate approach ,其基础是使用“elem=document.createElement('script')”,然后侦听elem.onreadyStateChange(对于ie)和elem.onload。将并行加载整批脚本,然后在它们都在的时候触发回调。在过去使用过这种方法,当时浏览器仍然主要是一次加载一个脚本来加快加载速度(它基于一个在别处有类似用途的脚本)。使用实例:

    Loader.script( [
        './js/jquery/jquery.jmap.js',
        './js/jquery/jquery.getUrlParam.js',
        './js/jquery/jquery.form.js',
        './js/jquery/jquery.gettext.js',
        './js/jquery/jquery.scrollTo.js',
        './js/jquery/jquery.prettydate.js'
      ], { complete: function() {
        // do your thing
      }
    })
    
        3
  •  0
  •   nopuck4you    15 年前

    一些可能的方法从我的头上。我要写步骤,而不是代码。

    选项1

    1. 通过将JS文件放在带有标记的元素中来加载它。
    2. 在触发setimout()后立即检查加载的JS文件中的变量。
    3. 重复2,直到变量存在或达到最大计数器(最大计数器=10),这样您就不会永远坐在那里。
    4. 一旦变量存在,继续下一个JS文件,并再次从步骤1开始。

    选项2

    1. 使用Ajax方法。
    2. 使用Ajax调用JS文件。
    3. 将结果附加到下面文章中的使用说明中。
    4. 将timeout()函数设置为几秒钟,然后处理脚本或检查与选项1类似的变量,并重复超时,直到出现变量为止。

    两种方法请参考本文: http://www.hunlock.com/blogs/Howto_Dynamically_Insert_Javascript_And_CSS

    困难的部分是,您要求您的代码知道浏览器何时加载和处理每个JS文件。由于计算机和浏览器会改变处理JS文件所需的时间,因此如果没有JS文件中的某些逻辑,就很难进行跟踪。

    我更喜欢这样的方法:

    . 制作一个JavaScript请求标签,AJAX,无论是什么服务器,都可以处理动态逻辑(PHP、Java、.NET等)。

    . 传递一个已经存在于页面上的回调函数,并知道需要知道多少额外的JS文件。甚至包括回调的索引参数(比如mycallback(5)),这样您就知道自己在哪里。

    1-2A . 您可以手动将回调函数附加到所有JS文件中,让函数管理JS队列,而不需要JS文件具有回调函数以外的任何知识。

    . 让服务器将回调附加到要返回的JS文件中。

    . 一旦JS被处理,回调函数将被触发,它将为您管理额外的JS文件的加载。

    那怎么样?