突然发现,其实本来自以为非常熟悉的一些技术,一旦被人提问以后就会发现自己并不能很好地说明清楚,所以开始认真地写一些非常基础的技术分享,不断查资料探究原理的过程还是非常有趣的

之前被问domReady事件到底是啥,比onload到底好在哪里,一句话说的话就是 DomTree加载完成之后,不等图片加载完domReady就会发生,这时绝大多数JS已经可以正常地运行了(因为页面上初始的dom元素都齐了)。但要说清楚这个domReady的来龙去脉,还真没那么简单。
W3C标准页面加载过程
W3C中关于浏览器加载过程的时间信息的接口标准中有很多页面加载方面的标准,简单地说在chrome里检查performance.timing就可以看见各个事件发生的时间戳

W3C的这张图很好地描述了浏览器加载页面的整个过程,除了domLoading(开始解析dom树)可能会抢先在responseEnd(网络接受应答完毕)之前发生之外,基本是从左到右时间顺序发生

这里我们关注processing以及之后的部分,我尝试解释一下各个事件的含义
domLoading浏览器拿到了一些HTML代码,开始解析dom树- 设置
document.readyState为”loading”(也就触发了readyStateChange) - 注意引用的外部script会阻塞dom树解析
- 解析过程比较复杂这里不多涉及,可以参考W3C的Parsing部分的标准
- 设置
domInteractive浏览器初步解析完dom树,但并不稳定,仍残余一些工作,W3C的相关标准链接- 设置
document.readyState为”interactive” - 清空打开标签堆栈,也就是强制关闭仍未闭合的标签
- 写在html中的script元素,含defer属性且不含async属性的会在这个时机被执行
- 此时才会触发
domContentLoaded事件
- 设置
domContentLoaded其实就是俗称的domReady事件,此时dom树已经稳定- aync属性的script元素在这个时机被执行
- 满足另一种条件的script元素也是这个时机被执行,我没能看懂,估计是来自于
document.createElement创建出来的某种情况
domComplete需要加载的资源(包括图片、iFrame等等等等)以及脚本全部执行完毕- 设置
document.readyState为”complete” - 这个事件和load事件的区别是load事件必须要有Browsing Context才会被触发,但时机是一样的
- 设置
loadEventStartload事件被触发loadEventEndload事件执行完毕
domReady的现实
一般来说我们希望页面上的脚本尽量快速地被执行,但很多情景下都需要完整可操作的dom树才能保证脚本执行正确,尤其是IE6在interactive以及之前的阶段,如果用JS操作了dom树会直接“无法打开站点”。
问题是绝大多数情况下,图片(或者swf等)都不是JS脚本需要的资源,也就是说我们要找的时间点是dom树完整之后,图片加载之前的一个地方来执行我们的脚本,也就是所谓的domReady。对于主流标准浏览器来说,domContentLoaded正是我们需要的domReady,但很不幸我们生活在有IE的世界里,所以需要为IE找一个替代品(doScroll checking)
jQuery相关源码解析
这里看的是jQuery1.7.2的代码,相关部分在core.js里面
jQuery里domReady的绑定代码是jQuery(callback)或者jQuery(document).ready(callback),最终都落到了jQuery.fn.ready上,readyList维护注册在ready时间上的callback集合,jQuery.ready是jQuery希望绑定在domReady上的callback,里面会执行所有通过各种手段绑定上ready的callback,于是我们主要看jQuery.bindReady
- 处理了被调用时已经load事件都发生过的情况(L430)
- 对标准浏览器,用domContentLoaded事件并同时也绑上load事件作为fallback来支持早期版本 (L437)
- 对IE,首先绑定了readystatechange的complete的情况,以及load事件,然后尝试使用
doScrollCheck来更早地触发ready事件(L445)
domReady的问题
domReady虽然通常比load快不少,但此时并不能保证外部资源全部load到,所以需要读img/包含img的元素的尺寸时不能直接在domReady里面跑,flash及类似的外部资源也有类似的问题
参考:DOM Ready 详解
