创建一个script元素...使用一个回调函数添加两个脚本。

13

我需要添加原型并添加scriptaculous,并在它们都完成加载时得到回调。 我目前是这样加载原型的:

var script = document.createElement("script");
script.src = "http://ajax.googleapis.com/ajax/libs/prototype/1.6.1.0/prototype.js";
script.onload = script.onreadystatechange = callback;
document.body.appendChild( script );

我可以通过链接回调函数来做到这一点,但那似乎是不好的实践(当我需要加载更多脚本时,我不想有一个傻傻的20个回调方法的链)。 有什么想法?


为什么不将这些脚本添加到HTML中,而不是使用JavaScript加载它们? - RaYell
2
我正在编写一小段代码,可以复制粘贴到其他人的HTML中以嵌入我的内容。我只想要一个空的div和对我的JavaScript文件的引用被复制粘贴进去。 - user199085
@kdiegert:你提到的js文件引用是指通过<script src="my.js"></script>这种方式吗? - Crescent Fresh
是的,需要粘贴的代码如下:<div id="myDiv"></div><script src="my.js?arguments=arguments"></script> - user199085
http://css-tricks.com/snippets/javascript/async-script-loader-with-callback/ - t1gor
4个回答

44

我建议您使用一些小型加载程序,它可以为您链接和执行操作。例如,像这样的:

function loadScripts(array,callback){
    var loader = function(src,handler){
        var script = document.createElement("script");
        script.src = src;
        script.onload = script.onreadystatechange = function(){
            script.onreadystatechange = script.onload = null;
            handler();
        }
        var head = document.getElementsByTagName("head")[0];
        (head || document.body).appendChild( script );
    };
    (function run(){
        if(array.length!=0){
            loader(array.shift(), run);
        }else{
            callback && callback();
        }
    })();
}

这个脚本可以帮助您构建脚本标签并在所有文件加载完成时调用回调函数。使用起来非常简单:

loadScripts([
   "http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js",
   "http://ajax.googleapis.com/ajax/libs/prototype/1.6.1.0/prototype.js"
],function(){
    alert('All things are loaded');
});

希望这能有所帮助


aysnc.parallel或async.series可能有助于此 https://github.com/caolan/async - Alexander Mills
在严格模式下,arguments.callee 会抛出错误。来自 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode "arguments.callee 不再被支持。在普通代码中,arguments.callee 指的是封闭函数。这种用法很弱:只需为封闭函数命名即可!此外,arguments.callee 实质上阻碍了像内联函数这样的优化,因为如果访问 arguments.callee,必须能够提供对未内联函数的引用。 - Gabriel Luethje
@GabrielLuethje,将其更改为命名函数 - nemisj
1
你能详细记录一下你在那里做了什么吗? - SumNeuron
这个解决方案在使用像Ember这样的SinglePageApplication时非常有用。 - JalilIrfan

2
由于Internet Explorer中的一个bug,nemisj的递归加载程序在IE中无法正常工作。可以通过在递归调用上设置延迟来解决问题,如下所示:

```javascript setTimeout(function() { recursiveFunction(); }, 0); ```

function loadScripts(array,callback){  
    var loader = function(src,handler){  
        var script = document.createElement("script");  
        script.src = src;  
        script.onload = script.onreadystatechange = function(){  
          script.onreadystatechange = script.onload = null;  
          <b>if(/MSIE ([6-9]+\.\d+);/.test(navigator.userAgent))window.setTimeout(function(){handler();},8,this);  
          else handler();  </b>
        }  
        var head = document.getElementsByTagName("head")[0];  
        (head || document.body).appendChild( script );  
    };  
    (function(){  
        if(array.length!=0){  
                loader(array.shift(),arguments.callee);  
        }else{  
                callback && callback();  
        }  
    })();  
}  

这个小技巧可以解决IE中常发生的无法解释的问题,在遇到这种情况时,通常就是这个方法能够解决。


1

由于scriptaculous需要prototype,您将不得不使用任何方法链接侦听器来加载这些脚本。

有各种脚本加载器可用于并行加载脚本,尽可能快地加载,例如LABjs,但在这种情况下没有一个能够提供很大的帮助。

如果最终需要加载10-20个脚本,我建议事先使用像Combiner这样的工具合并脚本。


实际上,这种情况正是LABjs设计的解决方案。 - Kyle Simpson

0
你可以将script.onload.onerror回调函数转换为Promise形式,然后使用Promise.all等待所有Promise都解决后再继续执行依赖这些脚本的代码。
以下是示例:

const loadScript = url => 
  new Promise((resolve, reject) => {
    const script = document.createElement("script");
    script.src = url;
    script.onload = resolve;
    script.onerror = e => 
      reject(Error(`${url} failed to load`))
    ;
    document.head.appendChild(script);
  })
;

const scriptURLs = [
  "https://unpkg.com/htm",
  "https://unpkg.com/axios@0.21.0/dist/axios.min.js",
  "https://cdnjs.cloudflare.com/ajax/libs/d3/6.3.1/d3.min.js",
  // ...
];

Promise.all(scriptURLs.map(loadScript))
  .then(async () => {
    /* Use your scripts here */
    console.log("proving scripts loaded:");
    axios.get("http://jsonplaceholder.typicode.com/users");
    console.log(d3.csvParse("a,b\n3,4\n5,6"));
    const html = htm.bind((t, p, ...c) => ({t, p, c}));
    console.log(html`<h1 id=hello>Hello world!</h1>`);
  })
  .catch(err => console.error(err))
;


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接