我想在整个页面加载之前运行一些 javascript。这可能吗?还是代码开始执行</html>
?
我可以在加载整个页面之前运行 javascript 吗?
不仅可以,但是你必须做出特别的努力,不,如果你不想。:-)
当浏览器script
在解析 HTML 时遇到经典标签时,它会停止解析并移交给运行脚本的 JavaScript 解释器。解析器在脚本执行完成之前不会继续(因为脚本可能会document.write
调用解析器应该处理的输出标记)。
这是默认行为,但您有一些延迟脚本执行的选项:
使用 JavaScript module。一个
type="module"
脚本被推迟,直到HTML已经完全解析和初始DOM创建。这不是使用module的主要原因,但这是原因之一:<script type="module" src="./my-code.js"></script> <!-- Or --> <script type="module"> // Your code here </script>
代码将被提取(如果它是单独的)并与 HTML 解析并行解析,但在 HTML 解析完成之前不会运行。(如果你的module代码是内联的,而不是在它自己的文件中,它也会被推迟到 HTML 解析完成。)
当我在 2010 年第一次写这个答案时,这不可用,但在 2020 年,所有主要的现代浏览器都原生支持module,如果你需要支持旧浏览器,你可以使用 Webpack 和 Rollup.js 之类的捆绑器。
defer
在经典脚本标签上使用该属性:<script defer src="./my-code.js"></script>
与module一样,代码
my-code.js
将与 HTML 解析并行获取和解析,但在 HTML 解析完成之前不会运行。但是,defer
不适用于内联脚本内容,仅适用于通过src
.我认为这不是您想要的,但是您可以使用该
async
属性告诉浏览器在解析 HTML 的同时获取 JavaScript 代码,然后尽快运行它,即使 HTML 解析未完成. 你可以把它放在一个type="module"
标签上,或者用它来代替defer
经典script
标签。将
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
在遇到标记之前不会开始获取代码,但现代浏览器会提前扫描并开始预取。尽管如此,这在这一点上仍然是第三种选择,这两个moduledefer
都是更好的选择。
该规范具有有用图表示原始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
。)
当您运行它时,您会看到“第 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
那里进行检查,因为任何现代浏览器支持module已经在forEach
上NodeList
。)
在这个现代世界中,DOMContentLoaded
PrototypeJS、jQuery、ExtJS、Dojo 和大多数其他人在当天提供(并且仍然提供)的“就绪”功能事件没有真正的value;只需使用module或defer
. 即使在过去,也没有太多理由使用它们(并且它们经常被错误地使用,在加载整个 jQuery 库时阻止页面呈现,因为它script
在head
文档中而不是在文档之后),一些开发人员在谷歌很早就标记了。这也是YUI 建议将脚本放在 . 末尾的部分原因body
,再次回到当天。
您可以随时运行 javascript 代码。AFAIK 它是在浏览器到达它所在的 <script> 标记的那一刻执行的。但是您无法访问尚未加载的元素。
所以如果你需要访问元素,你应该等到DOM被加载(这并不意味着整个页面都被加载了,包括图像和东西。它只是文档的结构,它加载得更早,所以你通常会赢没有注意到延迟),使用jQuery 中的DOMContentLoaded
事件或函数$.ready
。