【问题标题】:Can I run javascript before the whole page is loaded?我可以在整个页面加载之前运行 javascript 吗?
【发布时间】:2010-05-27 10:05:59
【问题描述】:

我想在整个页面加载之前运行一些 javascript。这可能吗?还是代码开始在</html>上执行?

【问题讨论】:

标签: javascript html


【解决方案1】:

不仅可以你,而且如果你不想这样做,你必须做出特殊的努力。 :-)

当浏览器在解析 HTML 时遇到经典的script 标签时,它会停止解析并移交给运行脚本的 JavaScript 解释器。解析器在脚本执行完成之前不会继续(因为脚本可能会执行 document.write 调用以输出解析器应该处理的标记)。

这是默认行为,但您有一些延迟脚本执行的选项:

  1. 使用 JavaScript 模块。 type="module" 脚本被延迟,直到 HTML 被完全解析并创建了初始 DOM。这不是使用模块的主要原因,但它是原因之一:

    <script type="module" src="./my-code.js"></script>
    <!-- Or -->
    <script type="module">
    // Your code here
    </script>
    

    代码将被提取(如果它是单独的)并与 HTML 解析并行解析,但在 HTML 解析完成之前不会运行。 (如果你的模块代码是内联的而不是在它自己的文件中,它也会被推迟到 HTML 解析完成。)

    当我在 2010 年第一次写这个答案时,这还不可用,但是在 2020 年,所有主要的现代浏览器都原生支持模块,如果您需要支持旧浏览器,您可以使用 Webpack 和 Rollup.js 等捆绑程序。

  2. 在经典脚本标签上使用defer 属性:

    <script defer src="./my-code.js"></script>
    

    与模块一样,my-code.js 中的代码将与 HTML 解析并行被提取和解析,但在 HTML 解析完成之前不会运行但是defer 不适用于内联脚本内容,只能用于通过src 引用的外部文件。

  3. 我认为这不是 想要的,但您可以使用 async 属性告诉浏览器与 HTML 解析并行获取 JavaScript 代码,但随后尽快运行它,即使 HTML 解析不完整。您可以将它放在type="module" 标签上,或者在经典的script 标签上使用它来代替defer

  4. script 标签放在文档末尾,就在结束&lt;/body&gt; 标签之前:

    <!doctype html>
    <html>
    <!-- ... -->
    <body>
    <!-- The document's HTML goes here -->
    <script type="module" src="./my-code.js"></script><!-- Or inline script -->
    </body>
    </html>
    

    这样,即使代码一遇到它就运行,它上面的 HTML 定义的所有元素都存在并且可以使用。

    过去,这会在某些浏览器上造成额外的延迟,因为它们在遇到 script 标记之前不会开始获取代码,但现代浏览器会提前扫描并开始预取。尽管如此,这仍然是第三个选择,模块和defer 都是更好的选择。

规范 has a useful diagram 显示了一个原始的 script 标记、deferasynctype="module"type="module" async,以及获取和运行 JavaScript 代码的时间:

这是一个默认行为示例,一个原始的script 标签:

.found {
    color: green;
}
<p>Paragraph 1</p>
<script>
    if (typeof NodeList !== "undefined" && !NodeList.prototype.forEach) {
        NodeList.prototype.forEach = Array.prototype.forEach;
    }
    document.querySelectorAll("p").forEach(p => {
        p.classList.add("found");
    });
</script>
<p>Paragraph 2</p>

(有关 NodeList 代码的详细信息,请参阅 my answer here。)

当你运行它时,你会看到“段落 1”是绿色的,但“段落 2”是黑色的,因为脚本与 HTML 解析同步运行,所以它只找到了第一段,而不是第二段。

相比之下,这是一个type="module" 脚本:

.found {
    color: green;
}
<p>Paragraph 1</p>
<script type="module">
    document.querySelectorAll("p").forEach(p => {
        p.classList.add("found");
    });
</script>
<p>Paragraph 2</p>

注意它们现在都是绿色的;在 HTML 解析完成之前,代码不会运行。对于带有外部内容(但不是内联内容)的 defer script 也是如此。

(这里不需要检查NodeList,因为任何支持现代浏览器的模块已经在NodeList 上有forEach。)

在这个现代世界中,PrototypeJS、jQuery、ExtJS、Dojo 和大多数其他人在当时提供(并且仍然提供)的“就绪”功能的 DOMContentLoaded 事件没有真正的价值;只需使用模块或defer。即使在过去,也没有太多使用它们的理由(而且它们经常被错误地使用,在加载整个 jQuery 库时暂停页面演示,因为 scripthead 中而不是在文档之后),some developers at Google 很早就标记了一些东西。这也是YUI recommendation 将脚本放在body 末尾的部分原因,再次回到过去。

【讨论】:

  • 或者全部放在一个函数里,放在&lt;body onload="doIt()&gt;"中。
  • @JustinLiu - load 事件发生在页面加载周期的后期非常,在此之前等待运行 JavaScript 几乎不是最佳实践。但是,是的,这是一种选择。顺便说一句,我已经彻底修改了 2020 年的答案。
  • 或者你可以使用document.addEventListener('DOMContentLoaded', function(){ //doyour stuff })
  • @JustinLiu - 是的,请参阅最后一段。 :-) 我从来没有看到有任何需要,但你可以。
  • @JustinLiu - 这开始过分了。如果您想发布答案,请发布答案。
【解决方案2】:

您可以随时运行 javascript 代码。 AFAIK 它在浏览器到达它所在的

所以如果你需要访问元素,你应该等到 DOM 被加载(这并不意味着整个页面被加载,包括图像和东西。它只是文档的结构,它加载得更早,所以您通常不会注意到延迟),在 jQuery 中使用 DOMContentLoaded 事件或 $.ready 之类的函数。

【讨论】:

  • DOMContentLoaded 事件将在 DOM 层次结构完全构建后立即触发,load 事件将在所有图像和子帧完成加载后触发。
猜你喜欢
  • 2011-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多