样式表的载入会延迟DOM载入事件

标签: CSS DOM JavaScript 事件 Firefox | 发表时间:2016-05-15 00:00 | 作者:Harttle
出处:http://harttle.com/

绝大多数情况下我们总是让JavaScript在DOM载入后再开始执行。 不管是直接用 DOM API 实现还是使用 jQuery,最终都是 DOMContentLoaded事件在起作用。 本文讨论一个我们习以为常却很少了解的问题: 样式文件的载入会延迟脚本执行,以及 DOMContentLoaded事件的触发。

DOMContentLoaded事件

页面文档(DOM)完全加载并解析完毕之后,会触发 DOMContentLoaded事件, HTML文档不会等待样式文件,图片文件,Iframe页面的加载。 但DOM树已被创建,多数JavaScript已经操作DOM并完成功能了。

This (DOMContentLoaded) event fires after the HTML code has been fully retrieved from the server, the complete DOM tree has been created and scripts have access to all elements via the DOM API. – molily.de

然而在绝大多数场景下,样式文件的载入会延迟 DOMContentLoaded事件的触发。 其实这样的行为正是开发者所希望的,为什么呢?

事实上,老版本的IE中 DOMContentLoaded事件存在兼容性问题。 参见: 兼容所有浏览器的 DOM 载入事件

浏览器为何延迟DOMContentLoaded

对于很多脚本而言,它们被编写时就希望在样式载入之后再开始执行。 JavaScript的作者往往会假设CSS规则已经生效,尤其是在进行一些显示相关的操作时, 比如需要得到DOM元素的位置和大小的场景。

事实上,在多数浏览器中 DOMContentLoaded事件的触发会考虑到外部样式文件的载入, 以及在HTML中脚本标签和样式标签的相对位置。 如果脚本位于样式之后,浏览器通常会认为该脚本依赖于样式的渲染结果, 也就更倾向于延迟脚本的执行(直到样式渲染结束)。

不同浏览器的行为

既然浏览器有时会延迟 DOMContentLoaded事件, 但是何时会延迟 DOMContentLoaded事件,还取决于行内脚本还是外部脚本,以及脚本与样式标签的相对位置。 不同的浏览器渲染引擎也有不同的行为。 在 http://molily.de/domcontentloaded/一文中对该问题有详尽的阐述和测试, 本文取其结论。

下表描述了各种情况下脚本是否会被延迟执行,进而延迟触发 DOMContentLoaded事件。

渲染引擎 样式表之前的脚本 样式表之后的外部脚本 样式表之后的行内脚本
Presto (Opera)
Webkit (Safari, Chrome)
Gecko (Firefox)
Trident (MSIE)  

HTML5标准及最佳实践

其实 DOMContentLoaded是Firefox中最先提出的, 此后JavaScript社区发现它确实比 load事件(要求所有资源完全载入)更好, 于是Apple和Opera相继开始支持该事件。 但不同浏览器的实现方式有所区别,于是产生了上表所示的复杂情况。

在HTML5标准中情况有所好转: DOMContentLoaded是一个纯DOM事件,与样式表无关。 与此同时,HTML5要求:

  • 脚本执行前,出现在当前 <script>之前的 <link rel="stylesheet">必须完全载入。
  • 脚本执行会阻塞DOM解析。

这样的话,假如脚本和样式一起放在HTML <head>中, DOM解析到 <script>标签时会阻塞DOM解析,开始如下操作:

  1. 获取当前 <script>的脚本文件;
  2. 获取并载入前面的所有 <link rel="stylesheet">
  3. 执行当前脚本文件。

这些操作完成之后才能继续进行DOM解析,解析完毕时再触发 DOMContentLoaded事件。 如果将样式和脚本都放到 <head>中,会使浏览器在渲染 <body>前载入并执行所有样式和脚本。 页面的显示延迟会很高,多数情况下用户体验都很糟糕。 因此在HTML5标准的HTML页面中,最佳实践应当是: 所有样式放在 <head>中;所有脚本放在 <body>最后。

jQuery文档中也推荐这样的实践方式。

参考阅读

相关 [样式表 延迟 dom] 推荐:

样式表的载入会延迟DOM载入事件

- - Harttle Land
绝大多数情况下我们总是让JavaScript在DOM载入后再开始执行. 不管是直接用 DOM API 实现还是使用 jQuery,最终都是 DOMContentLoaded事件在起作用. 本文讨论一个我们习以为常却很少了解的问题: 样式文件的载入会延迟脚本执行,以及 DOMContentLoaded事件的触发.

DOM详解

- - CSDN博客推荐文章
 1.XML解析方式分为两种:dom和sax.   (1)dom:(Document Object Model, 即文档对象模型) 是 W3C 组织推荐的处理 XML 的一种方式.   (2) sax:(Simple API for XML) 不是官方标准,但它是 XML 社区事实上的标准,几乎所有的 XML 解析器都支持它.

javaScript DOM使用

- - CSDN博客互联网推荐文章
通过 HTML DOM,可访问 JavaScript HTML 文档的所有元素. 1 修改HTML元素内容. document.write(Date()); //在输入流中直接写 document.getElementById(id).innerHTML=new HTML. //改变已经有的元素内容 document.getElementById("image").src="landscape.jpg";.

DOM优化

- - JavaScript - Web前端 - ITeye博客
1,Javascript语音与DOM操作就像孤岛. 他们之间的通行是要借助船的,而使用船是有很大成本的. 所以,要先做完一个再做另一个,最好不要交替进行. 如:添加1000个li时,先用一个字符串拼接好,最后一次追加到父节点,而不是向父节点追加1000次,一次追加一个. 如:添加1000个li时,先放到fragment,再添加到UL中.

Android DOM解析XML

- - CSDN博客移动开发推荐文章
if(personChilds.item(y).getNodeType()==Node.ELEMENT_NODE){//判断当前节点是否是元素类型节点. 作者:jaycee110905 发表于2013-2-7 21:04:29 原文链接. 阅读:78 评论:0 查看评论.

jquery和DOM比较

- - JavaScript - Web前端 - ITeye博客
1、window.onload和$(document).ready()的区别. 必须等整个网页中所有的内容加载完毕后(包括图片)才能执行. 网页中所有DOM结构绘制完毕后就执行,可能DOM元素并没有加载完. 2、jquery对象和DOM对象的区别. Juery对象是包装DOM后的产生的对象,DOM是原生对象,是一个基本的文档结构.

Javascript的DOM操作

- - CSDN博客Web前端推荐文章
返回对拥有指定id的第一个对象进行访问. 返回带有指定名称的节点集合. 返回带有指定标签名的对象集合. 返回带有指定class名称的对象集合. 参数:是否复制原节点的所有属性. 注意:IE会忽略节点间生成的空白文本节点(例如,换行符号),而Mozilla不会这样做. 在删除指定节点的时候不会出错,但是如果要删除最后一个子结点或者是第一个子结点的时候,就会出现问题.

DOM世界的观察者

- - 博客园_Ruby's Louvre
浏览器自带的观察者实在太多了. 经典的不用说,就是onclick, attachEvent, addEventListner,可惜它们只是监听用户的行为. 不过这当中有个特例是propertychange,当元素的属性,不管是自定义还是原生,只要发生改变,就会触发回调. 我们还可以通过它的事件对象的propertyName知道那个元素发生改变.

javascript之XML DOM的解析

- - ITeye博客
javascript之XML DOM的解析. . 长春. 吉林市. 四平. 松原. 通化. .

也许,DOM 不是答案

- - 阮一峰的网络日志
有一个词"手机网站"(mobile web),指供手机浏览的网站,但它是不存在的. 人们提到"移动互联网"的时候,其实专指另外一样东西:手机App. 一、Web App vs. 比起手机App,网站有一些明显的优点. 快速部署:升级只需在服务器更新代码. 超链接:可以与其他网站互连,可以被搜索引擎检索.