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


Answers:


186

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

当浏览器script在解析HTML时遇到经典标记时,它将停止解析并移交给运行脚本的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"标签上,也可以代替defer经典script标签使用。

  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定义的所有元素都存在并且可以使用。

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

该规范具有有用图表示原始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代码的。)

运行该脚本时,您会看到“段落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检查,因为任何现代浏览器支持模块都已forEach启用NodeList。)

在当今这个现代世界中,DOMContentLoadedPrototypeJS,jQuery,ExtJS,Dojo和大多数其他早在今天提供的“就绪”功能并没有真正的价值。只需使用模块或defer。甚至在今天,也没有太多的理由使用它们(它们经常被错误地使用,因为整个jQuery库都在文档中而不是在文档之后,所以它们script在加载整个jQuery库时阻止了页面演示head),有些开发人员Google早些时候就举报了。这也是YUI建议将脚本放到末尾的原因之一body,也是在今天。


1
@FeRD-感谢您的编辑。审稿人拒绝了它,但您是正确的。:-)
TJ Crowder

或者将其全部放入一个函数中并放入<body onload="doIt()>"
贾斯汀·刘

@JustinLiu -该load事件发生非常晚在页面加载的过程,这是几乎从来没有最好的做法,以等待运行你的JavaScript在那之前。但是是的,这是一个选择。顺便说一句,我已经彻底审查了2020年的答案。
TJ Crowder

或者您可以使用document.addEventListener('DOMContentLoaded', function(){ //doyour stuff })
Justin Liu

@JustinLiu-是的,请参阅最后一段。:-)我从未见过任何需要,但可以。
TJ Crowder

6

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

因此,如果您需要访问元素,则应等待DOM加载完毕(这并不意味着整个页面都已加载,包括图像和其他内容。这只是文档的结构,它的加载时间要早得多,因此通常您会赢(没有注意到延迟),请使用jQuery中的DOMContentLoaded事件或函数$.ready


2
一旦完全构建了DOM层次结构,就会触发DOMContentLoaded事件,当所有图像和子帧都完成加载后,load事件就会执行该事件。
BeauCielBleu 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.