<script defer =“ defer”>是如何工作的?


208

我有几个<script>元素,其中一些代码取决于其他<script>元素中的代码。我看到该defer属性在这里可以派上用场,因为它允许代码块推迟执行。

为了测试它,我在Chrome上执行了此操作:http : //jsfiddle.net/xXZMN/

<script defer="defer">alert(2);</script>
<script>alert(1)</script>
<script defer="defer">alert(3);</script>

但是,它会发出警报2 - 1 - 3。为什么它不警报1 - 2 - 3


2
也许看看这篇文章。而且,与往常一样,IE对某些含义有自己的看法,因此决定先加载脚本,但将执行延迟到加载正文之前(通常)。
布拉德·克里斯蒂

谢谢,但是测试页在Chrome上的结果不同:websiteoptimization.com/speed/tweak/defer/test。屏幕截图显示了我的期望,而Chrome似乎只是先执行了延迟的操作。
pimvdb 2011年

1
我想您会发现IE的defer定义与DOM Level 1规范中W3C的defer意图相符。
2011年

41
正如Alohci在其答案中已经指出的那样,根据HTML标准 defer,只有在指定时才有效src。这可能是您的示例在大多数浏览器中无法按预期工作的原因。
Pankrat 2011年

2
@Pankrat真实的故事!尽量jsfiddle.net/xXZMN/50在Firefox24测试
m93a

Answers:


51

更新日期:2016/2/19

考虑这个答案已经过时了。请参阅此帖子上的其他答案,以获取与更新的浏览器版本相关的信息。


基本上,defer告诉浏览器在执行该脚本块中的javascript之前先等待“就绪”。通常这是在DOM完成加载和document.readyState == 4之后

defer属性特定于Internet Explorer。在Windows 7的Internet Explorer 8中,我在JS Fiddle测试页中看到的结果是1-2-3。

结果可能因浏览器而异。

http://msdn.microsoft.com/zh-CN/library/ms533719(v=vs.85).aspx

与流行的看法相反,IE遵循标准的频率高于人们所允许的水平,实际上,“延迟”属性是在DOM级别1规范中定义的http://www.w3.org/TR/REC-DOM-Level-1/level -one-html.html

W3C对延迟的定义:http : //www.w3.org/TR/REC-html40/interact/scripts.html#adef-defer

“设置为boolean属性时,该布尔属性向用户代理提示该脚本将不会生成任何文档内容(例如,javascript中没有“ document.write”),因此,用户代理可以继续解析和呈现。


8
@ MarkAtRamp51-如果您的答案已过时,则应对其进行编辑,而不要在其他答案的评论中抱怨投票不足。下注是针对“无用”的答案。
Christian Conkle

10
@ChristianConkle我感谢礼节课,但是这里的其他答案是最新的。我正在解决一个事实,即在提出问题时未选择错误的答案。也许您应该警告那些散布关于社区的错误评估的人,错误地选择答案,而不是人们试图提醒人们事情会随着时间的推移而变化,并且环境很重要。我看不出删除答案的价值,因为历史信息也很有价值。
2015年

3
“由于历史信息也很有价值,我认为删除答案也没有价值。”在这种情况下,如何在开头添加注释,指出该注释仅适用于HTML5之前的版本,然后链接到“正确的”(最新)答案?那应该为您省去很多麻烦(以一个曾经接受过“错误”答案的人说话,并且最终受到“压力”的改变)。
mgibsonbr

3
@Leo应该不被标记吗?通过搜索“ html5延迟脚本”,这是google中的第三个结果。然后,此答案为许多用户提供了过时且错误的定义。(当前定义:“指示用户代理可以延迟脚本的处理。请参见HTML 4.0中的延迟属性定义。”)。
马拉沃斯2015年

2
@ MarkAtRamp51我认为您应该更新答案。找到该问题的任何人,因此您的答案将无法识别其历史信息。在他们看来,这就是今天正确的答案。互联网就是这样运作的。因此,您应该编辑答案,注意曾经是正确的答案,然后参考正确的答案。
Juuro 2015年

167

HTML5规范的一些摘要:http : //w3c.github.io/html/semantics-scripting.html#element-attrdef-script-async

如果不存在src属性,则不得指定defer和async属性。


使用这些属性可以选择三种可能的模式[async和defer]。如果存在async属性,则脚本将在可用时立即异步执行。如果async属性不存在,而defer属性存在,则在页面解析完成后执行脚本。如果这两个属性都不存在,则在用户代理继续解析页面之前,将立即获取并执行脚本。


由于大多数历史原因,这些属性的确切处理细节有些微不足道,涉及HTML的许多方面。因此,实施要求必须分散在整个说明书中。下面的算法(在本节中)描述了此处理的核心,但是这些算法引用了HTML脚本中的开始和结束标记的解析规则,并以外部内容和XML的文档规则引用了这些算法。 ()方法,脚本处理等


如果该元素具有src属性,并且该元素具有defer属性,并且该元素已被标记为“插入分析器”,则该元素没有async属性:

该元素必须添加到脚本列表的末尾,该脚本列表将在文档完成与创建该元素的解析器的Document关联的解析后执行。


37
也许,由于您的评论无用,我在这里的回答将阻止人们对我的回答投反对票。公认的答案没有错,答案是不同的,因为在2011年初,HTML5规范与主流Web浏览器的相关性要比当前降低。这个答案可能会更好,但是从任何标准来看,公认的答案都不对。
在Mark Ramp51

3
虽然了解规范说明很有用,但事实证明,某些浏览器(如IE <9)实现defer得很差。如果使用defer,则不能依赖某些浏览器中按顺序执行的脚本文件。
Flimm

2
@Flimm不只是IE,似乎Firefox也不保证执行顺序
富兰克林于

第一个报价无效了吧?现在,我可以阅读以下内容:“如果不存在src属性,或者脚本不是经典脚本,则不能指定该属性。” 而且经典脚本也是没有src =“”的脚本。
费利克斯·桑斯

158

真正的答案是:因为您不能相信延期。

在概念上,延迟和异步的区别如下:

异步允许脚本在后台下载而不会阻塞。然后,在完成下载的那一刻,渲染被阻止并且该脚本执行。执行脚本后,将继续渲染。

除了声明可以保证脚本按在页面上指定的顺序执行以及声明将在文档解析完成后执行之外,defer的作用相同。因此,某些脚本可能会完成下载,然后坐下来等待后来下载但出现在它们之前的脚本。

不幸的是,由于实际上是一场标准的斗争,defer的定义因规格而异,甚至在最新的规格中也无法提供有用的保证。由于答案在这里这个问题表明,浏览器实现延迟是不同的:

  • 在某些情况下,某些浏览器会存在一个错误,导致defer脚本无法正常运行。
  • 有些浏览器将DOMContentLoaded事件延迟到defer脚本加载之后,有些则没有。
  • 有些浏览器服从defer<script>与内嵌代码,并没有元素src属性,有些忽略它。

幸运的是,规范至少指定了异步优先于延迟。因此,您可以将所有脚本视为异步脚本,并获得广泛的浏览器支持,如下所示:

<script defer async src="..."></script>

全球使用的浏览器中有98%,在美国使用的浏览器中有99%将避免使用此方法进行阻止。

(如果您需要等到文档完成解析后,请监听事件DOMContentLoaded事件或使用jQuery的便捷.ready()功能。无论如何,您都希望这样做,以在根本不实现的浏览器上优雅地返回defer。)


13
谢谢,您的回答对我最有帮助!
markus 2012年

5
我相信这是不正确的。defer的好处在于,它在页面解析完成之前不会执行。这个页面有视觉解释异步和延迟之间的区别了良好:peter.sh/experiments/...
tinkerr

1
@tinkerr在概念上您是正确的;实际上,事实并非如此。因为没有一致地实现,所以序列保证不是通用的,因此也不是保证。当执行某些东西时,您会在意执行。设计的意图很可爱,但并不是特别有用。
克里斯·莫斯基尼

只是想指出,Opera defer自2013年6月2日发布15版以来就支持该属性。

1
@VikasBansal对于不支持异步的旧版浏览器-即旧版IE。
克里斯·莫斯基尼


8

由于defer属性仅适用于带有src的脚本标签。找到了一种模仿内联脚本延迟的方法。使用DOMContentLoaded事件。

<script defer src="external-script.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function(event) {
    // Your inline scripts which uses methods from external-scripts.
});
</script>

这是因为,完全延迟属性脚本加载后会触发DOMContentLoaded事件。


6

defer属性仅适用于外部脚本(仅当存在src属性时才应使用)。



4

看看Google开发商Jake Archibald在2013年撰写的这篇出色的文章,深入探究脚本加载的黑暗世界。

引用该文章的相关部分:

延期

<script src="//other-domain.com/1.js" defer></script>
<script src="2.js" defer></script>

Spec说:一起下载,在DOMContentLoaded之前按顺序执行。忽略没有“ src”的脚本的“ defer”。

IE <10说:我可能会在执行1.js的途中执行2.js。那不是很有趣吗?

红色的浏览器:我不知道这是什么“推迟”的事情,我将加载脚本,就像它不在那里一样。

其他浏览器说:好的,但是我可能不会忽略没有“ src”的脚本的“ defer”。

根据此评论defer,我将添加早期版本的Firefox在脚本完成运行之前触发DOMContentLoaded 。)

现代的浏览器似乎已async正确支持,但是您需要对脚本运行无序并且可能在DOMContentLoaded之前没有问题。


1

设置此布尔值属性是为了向浏览器指示脚本将在文档被解析后执行。由于所有其他主流浏览器尚未实现此功能,因此作者不应该假定脚本的执行实际上会被推迟。永远不要从延迟脚本中调用document.write()(因为Gecko 1.9.2起,这会吹走文档)。在没有src属性的脚本上,不应使用defer属性。从Gecko 1.9.2开始,在没有src属性的脚本上将忽略defer属性。但是,在Gecko 1.9.1中,如果设置了defer属性,即使内联脚本也会被延迟。

defer适用于chrome,firefox,即> 7和Safari

参考:https : //developer.mozilla.org/en-US/docs/HTML/Element/script


0

defer属性是布尔属性。

如果存在,它指定在页面完成解析后执行脚本。

注意:defer属性仅适用于外部脚本(仅当存在src属性时才应使用)。

注意:可以通过多种方式执行外部脚本:

如果存在异步:脚本与页面的其余部分异步执行(脚本将在页面继续解析的同时执行)如果不存在异步并且存在defer:在页面完成解析后执行脚本都不存在异步或延迟:在浏览器继续解析页面之前,立即获取并执行脚本

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.