【发布时间】:2010-05-27 10:05:59
【问题描述】:
我想在整个页面加载之前运行一些 javascript。这可能吗?还是代码开始在</html>上执行?
【问题讨论】:
-
我不认为这是重复的 - 它更多的是服务器-客户端通信问题。
标签: javascript html
我想在整个页面加载之前运行一些 javascript。这可能吗?还是代码开始在</html>上执行?
【问题讨论】:
标签: javascript html
不仅可以你,而且如果你不想这样做,你必须做出特殊的努力不。 :-)
当浏览器在解析 HTML 时遇到经典的script 标签时,它会停止解析并移交给运行脚本的 JavaScript 解释器。解析器在脚本执行完成之前不会继续(因为脚本可能会执行 document.write 调用以输出解析器应该处理的标记)。
这是默认行为,但您有一些延迟脚本执行的选项:
使用 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 等捆绑程序。
在经典脚本标签上使用defer 属性:
<script defer src="./my-code.js"></script>
与模块一样,my-code.js 中的代码将与 HTML 解析并行被提取和解析,但在 HTML 解析完成之前不会运行。 但是,defer 不适用于内联脚本内容,只能用于通过src 引用的外部文件。
我认为这不是 您 想要的,但您可以使用 async 属性告诉浏览器与 HTML 解析并行获取 JavaScript 代码,但随后尽快运行它,即使 HTML 解析不完整。您可以将它放在type="module" 标签上,或者在经典的script 标签上使用它来代替defer。
将script 标签放在文档末尾,就在结束</body> 标签之前:
<!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 标记、defer、async、type="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 库时暂停页面演示,因为 script 在 head 中而不是在文档之后),some developers at Google 很早就标记了一些东西。这也是YUI recommendation 将脚本放在body 末尾的部分原因,再次回到过去。
【讨论】:
<body onload="doIt()>"中。
load 事件发生在页面加载周期的后期非常,在此之前等待运行 JavaScript 几乎不是最佳实践。但是,是的,这是一种选择。顺便说一句,我已经彻底修改了 2020 年的答案。
document.addEventListener('DOMContentLoaded', function(){ //doyour stuff })
您可以随时运行 javascript 代码。 AFAIK 它在浏览器到达它所在的
所以如果你需要访问元素,你应该等到 DOM 被加载(这并不意味着整个页面被加载,包括图像和东西。它只是文档的结构,它加载得更早,所以您通常不会注意到延迟),在 jQuery 中使用 DOMContentLoaded 事件或 $.ready 之类的函数。
【讨论】: