【问题标题】:Weird behavior on script insertion in htmlhtml中脚本插入的奇怪行为
【发布时间】:2020-03-13 21:01:33
【问题描述】:

我想在页脚 html 部分插入 2 个脚本,但我的行为很奇怪。如果我在 html 本身中使用标签手动插入,它可以工作(index1.html)。但是,如果我使用 IIFE 创建标签并将它们插入相同的位置和相同的顺序 (index2.html),控制台中会显示一个错误,告诉“未定义虚拟变量”。常识告诉我错误是可以的(因为dummy是在使用后定义的),但是为什么它适用于index1.html?

index1.html:

    ...
    <script src="script1.js" async></script>
    <script src="script2.js"></script>
    </body>
</html>

index2.html:

    ...
    <script>
        (function(){
            var loadScript = function(data, callback) {
                var script = document.createElement('script');
                script.src = data.src;
                if (data.opts) {
                    if (data.opts.async) script.async = data.opts.async;
                }
                document.body.appendChild(script);
            }

            var loadScriptRecursive = function(scripts, index) {
                loadScript(scripts[index], function () {
                    if (++index < scripts.length) loadScriptRecursive(scripts, index);
                });
            }

            var scripts = [{src: 'script1.js'}, {src: 'script2.js', opts: {async: false}}];
            loadScriptRecursive(scripts, 0);
        })()
    </script>

    </body>
</html>

script1.js:

$(document).ready(function () {
    if (dummy) ...
    ...
}

script2.js:

var dummy = true

【问题讨论】:

  • 会不会,因为第一个脚本是异步的?
  • 我已经读到使用 document.createElement('script') 插入默认是异步的,这就是为什么我将 async=false 设置为第二个
  • 是的,你给自己一个竞争条件。
  • 检查一下:javascript.info/script-async-defer 说“动态脚本默认表现为‘异步’。”
  • 是的,我的意思是,当无法保证脚本的顺序时,您不应该期望脚本的顺序很重要。你需要重写你的代码,所以这个顺序无关紧要。

标签: javascript html iife


【解决方案1】:

首先,您声明callback,但从来没有我们。我假设你的意思是这样的script.onload = callback;

(function() {
  var loadScript = function(data, callback) {
    var script = document.createElement("script");
    script.src = data.src;
    script.onload = callback; // NEW LINE
    if (data.opts) {
      if (data.opts.async) script.async = data.opts.async;
    }
    document.body.appendChild(script);
  };

  var loadScriptRecursive = function(scripts, index) {
    loadScript(scripts[index], function() {
      if (++index < scripts.length) {
        loadScriptRecursive(scripts, index);
      }
    });
  };

  var scripts = [
    { src: "script1.js" },
    { src: "script2.js", opts: { async: false } }
  ];
  loadScriptRecursive(scripts, 0);
})();

但这还不足以解决这个问题,因为$(document).ready()

让我们看看文档是怎么说的:

.ready() 方法提供了一种在页面的文档对象模型 (DOM) 变得可以安全操作时立即运行 JavaScript 代码的方法。 [jQuery]

当初始 HTML 文档完全加载和解析时触发 DOMContentLoaded 事件,无需等待样式表、图像和子框架完成加载。 [MDN]

在第一个示例中,DOMContentLoaded 在 JS 文件加载后触发:

在第二个示例中,DOMContentLoaded 立即触发,无需等待 JS 文件:


这就是为什么你需要使用$(window).on("load", function() {}); 而不是$(document).ready(function() {});

【讨论】:

  • 看起来不错,对我有用,但阅读您留下的 jquery 链接,$(window).on("load", handler)says:“当此事件触发时,它表明页面上的所有资产都已加载,包括图像”,这是不同的DOMContentLoaded你解释了
  • 你描述的事件是这样的吗? load 等到其余文件下载完毕
  • 它在页面内容不包含加载其他内容(如图像、字体等)时有效。但如果不是这种情况,启动该部分代码将有很长的延迟
  • 是的,load 事件在所有内容加载后触发,DOMContentLoaded 仅在 DOM 可以安全操作时触发。如果您使用 JS 添加脚本,那么您的 DOM 已经可以安全使用,这就是为什么DOMContentLoaded 事件会立即触发。
  • 您可以更改脚本的顺序并确保所有依赖项都已准备好用于script1.js,这可能对DOMContentLoadedloadScript 有所帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-25
  • 1970-01-01
  • 1970-01-01
  • 2013-11-05
  • 2018-04-06
  • 1970-01-01
  • 2019-08-24
相关资源
最近更新 更多