我可以在加载整个页面之前运行javascript吗?

2022-08-30 04:46:40

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


答案 1

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

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

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

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

    <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>
    

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

  3. 我不认为这是想要的,但你可以使用该属性告诉浏览器与HTML解析并行获取JavaScript代码,但即使HTML解析不完整,也要尽快运行它。您可以将其放在标记上,也可以使用它而不是经典标记。asynctype="module"deferscript

  4. 将标记放在文档末尾,紧挨着结束标记之前: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定义的所有元素都存在并准备好使用。

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

该规范有一个有用的图表,显示了原始标记,,,,以及获取和运行JavaScript代码的时间:scriptdeferasynctype="module"type="module" async

enter image description here

下面是默认行为(原始标记)的示例: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 解析完成之前,代码才运行。对于具有外部内容(但不是内联内容)的 a 也是如此。deferscript

(那里不需要对 NodeList 进行检查,因为任何支持模块的现代浏览器已经在 NodeList 上具有 forEach

在这个现代世界中,PrototypeJS,jQuery,ExtJS,Dojo和大多数其他功能在当时提供(并且仍然提供)的“就绪”功能的事件没有真正的价值;只需使用模块或.即使在当时,也没有太多理由使用它们(而且它们经常被错误地使用,在整个jQuery库加载时会暂停页面呈现,因为它们在文档中而不是之后),这是Google的一些开发人员很早就标记出来的。这也是 YUI 建议将脚本放在 末尾的部分原因,这在当时再次出现。DOMContentLoadeddeferscriptheadbody


答案 2

您可以随时运行javascript代码。AFAIK 它在浏览器到达<script>标记时执行。但是,您无法访问尚未加载的元素。

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