使用远程 src 注入脚本标签并等待它执行

IT技术 javascript
2021-02-11 18:04:32

如何将<script src="https://remote.com/"></script>元素注入我的页面,等待它执行,然后使用它定义的函数?

仅供参考:就我而言,脚本会在极少数情况下进行一些信用卡处理,所以我不想总是包含它。我想在用户打开 change-credit-card-options 对话框时快速包含它,然后将新的信用卡选项发送给它。

编辑其他详细信息:我无权访问远程脚本。

6个回答

您可以使用Google AnalyticsFacebook的方法:

(function(d, script) {
    script = d.createElement('script');
    script.type = 'text/javascript';
    script.async = true;
    script.onload = function(){
        // remote script has loaded
    };
    script.src = 'http://www.google-analytics.com/ga.js';
    d.getElementsByTagName('head')[0].appendChild(script);
}(document));

更新:

以下是新的 Facebook 方法;它依赖于现有的脚本标签而不是<head>

(function(d, s, id){
    var js, fjs = d.getElementsByTagName(s)[0];
    if (d.getElementById(id)){ return; }
    js = d.createElement(s); js.id = id;
    js.onload = function(){
        // remote script has loaded
    };
    js.src = "//connect.facebook.net/en_US/sdk.js";
    fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
  • 替换facebook-jssdk为您唯一的脚本标识符,以避免它被多次附加。
  • 将脚本的 url 替换为您自己的。
请注意,如果脚本已经存在,d.getElementsByTagName(s)[0]可以将第一个查询移动到if检查下方以保存自己的 DOM 查询。
2021-03-13 18:04:32
你为什么不把这个附加到身体上?
2021-03-18 18:04:32
这看起来很完美,但互联网上有一些关于并非所有浏览器都调用 onload 的传言。你知道这适用于哪些浏览器吗?
2021-03-29 18:04:32
我会向事件添加一个事件侦听器,而不是使用script.onloador stackoverflow.com/questions/15564029/...js.onloadload
2021-04-08 18:04:32
script将其附加到另一个与将其附加到head. 我认为附加到scripts的缺点是如果没有任何附加到它会中断。
2021-04-09 18:04:32

使用事件侦听器和 ES2015 构造的相同方法:

function injectScript(src) {
    return new Promise((resolve, reject) => {
        const script = document.createElement('script');
        script.src = src;
        script.addEventListener('load', resolve);
        script.addEventListener('error', e => reject(e.error));
        document.head.appendChild(script);
    });
}

injectScript('https://example.com/script.js')
    .then(() => {
        console.log('Script loaded!');
    }).catch(error => {
        console.error(error);
    });
@dar0x 你检查开发控制台了吗?它可能会被站点的内容安全策略阻止。
2021-03-18 18:04:32
好的!这将是一个很好的 NPM 包或 ES6 module。+1 用于示例用法。
2021-03-19 18:04:32
您知道为什么在 then() 主体中我不能使用加载脚本中定义的变量/函数吗?
2021-04-09 18:04:32

这是一种动态加载和同步执行脚本列表的方法。您需要将每个脚本标签插入到 DOM 中,将其 async 属性显式设置为 false:

script.async = false;

默认情况下,已注入 DOM 的脚本是异步执行的,因此您必须手动将 async 属性设置为 false 来解决此问题。

例子

<script>
(function() {
  var scriptNames = [
    "https://code.jquery.com/jquery.min.js",
    "example.js"
  ];
  for (var i = 0; i < scriptNames.length; i++) {
    var script = document.createElement('script');
    script.src = scriptNames[i];
    script.async = false; // This is required for synchronous execution
    document.head.appendChild(script);
  }
  // jquery.min.js and example.js will be run in order and synchronously
})();
</script>

<!-- Gotcha: these two script tags may still be run before `jquery.min.js`
     and `example.js` -->
<script src="example2.js"></script>
<script>/* ... */<script>

参考

它现在正在工作,已投票。顺便说一句,最后一个关闭脚本缺少反斜杠。
2021-04-08 18:04:32

动态的 import()

使用动态导入,您现在可以加载module并等待它们执行,就像这样:

import("http://example.com/module.js").then(function(module) {
  alert("module ready");
});

如果module已经被加载和执行,它不会被再次加载和执行,但是返回的promiseimport仍然会解析。

请注意,该文件是作为module加载的,而不仅仅是脚本。module以严格模式执行,并在module范围内加载,这意味着变量不会像在正常加载的脚本中那样自动成为全局变量。export在module中使用关键字与其他module或脚本共享变量。

参考:

这样的事情应该可以解决问题:

(function() {
    // Create a new script node
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.onload = function() {
        // Cleanup onload handler
        script.onload = null;

        // do stuff with the loaded script!

    }

    // Add the script to the DOM
    (document.getElementsByTagName( "head" )[ 0 ]).appendChild( script );

    // Set the `src` to begin transport
    script.src = "https://remote.com/";
})();

希望有帮助!干杯。

是的,这是为了避免旧 IE 中的内存泄漏,因为脚本元素将函数引用为 onload 处理程序。
2021-03-25 18:04:32
您是否有理由在回调中手动将脚本 onload 设置为 null?它是否有问题不止一次触发,或者这只是强制某种垃圾收集?
2021-04-12 18:04:32