缘起

上周面了上海一家大型互联网公司,并顺利拿到了达到预期的 Offer。

不过这并不是重点,

CTO 本尊的一个技术问题让我印象深刻:“JQuery 中的 DomReady 在什么时候触发?”。

今天抽空梳理一下。

若干年前踩过的小坑

不失一般性,在我开始接触前端的时候,学会的第一个框架或者类库 ——Jquery。每次用 JQ 时候用 $(document).ready() 对于逻辑代码进行包裹几乎也是标配。很长一段时间都没有深究过,只想当然的认为加上这个代码块包裹就是等文档准备完成在执行内部逻辑。和原生的window.onload 差不多,甚至猜测其就是对于原生onload 方法的封装。

然而。。。

上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport" />
<title>test3</title>
<script src = "http://g.alicdn.com/sj/lib/jquery/dist/jquery.min.js"></script>
</head>
<body>
<div>
<img src= "http://img5.imgtn.bdimg.com/it/u=950694448,746521765&fm=21&gp=0.jpg"/>
</div>
<script>
$(document).ready(function(){ //文档树加载完
//console.log('ready');
var height = $('body').height();
console.log('height:::',height); // -> ???
})
</script>
</body>
</html>

你会发现在 Chrome 的控制台打出的不是一个确定的值(时而是222,时而是16)。

宝宝当时就蒙了。。。

$(document).ready何时触发?

Copy 了 Jquery 源码 https://github.com/jquery/jquery/blob/master/src/core/ready.js 中的代码片段如下:

1
2
3
4
5
6
7
8
9
10
11
<script>
if ( document.readyState === "complete" ||
( document.readyState !== "loading" && !document.documentElement.doScroll ) ) {
//Handle it asynchronously to allow scripts the opportunity to delay ready;
window.setTimeout( jQuery.ready );
} else { //Use the handy event callback
document.addEventListener( "DOMContentLoaded", completed );
//A fallback to window.onload, that will always work
window.addEventListener( "load", completed );
}
</script>

可以看到:我们常用的 jQuery 的$(document).ready()方法,就是对 DOMContentLoaded 事件的监听(当然,其内部还会提供其他降级方案)。扩展阅读:jQuery 的 ready 函数是如何工作的?

DOMContentLoaded事件的触发条件是:“The DOMContentLoaded event is fired when the initial HTML document has been completely loaded and parsed, without waiting for stylesheets, images, and subframes to finish loading. Note: Synchronous Javascript pauses parsing of the DOM.” https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded
这就意味着:DOMContentLoaded事件将会在所有的 DOM 全部加载完毕并且同步的 JS 加载执行后触发,并不会等待样式和图片的加载。

所以这就解释了前文里面为什么height()会 get 不到正确的高度了,因为 img 还没加载好啊!!!

PS: 如果 js 是通过动态加载进来的话,不会影响到DOMContentLoaded的触发时间,换句话说:动态加载 JS 可提前触发该事件。

window.onload何时触发?

“They fire after all objects in the DOM hierarchy (images, sub-frames, …) have finished loading and the document object has been built up. ”

详细请见:

http://help.dottoro.com/ljawbmqm.php

DOM 的加载详细过程

1.首先浏览器会解析静态的 html 结构 比如 head body footer等 html 标签

2.当 html 结构加载完成我们需要渲染页面使其美观,因此此时需要加载 各种样式表文件。

3.然后再解析并执行 js 或者其他脚本代码。

4.构造 html dom 对象,也就是 ready 操作,自此 DOMContentLoaded 被触发。

5.加载 html 和 css 中引用的外部资源文件(img 等)。

6.页面加载完成( 然后 window.onload 被触发),js 进行一些事件的绑定和处理。

Reference:

https://developer.mozilla.org/en-US/docs/Web/Events/DOMContentLoaded

http://www.alloyteam.com/2014/03/effect-js-css-and-img-event-of-domcontentloaded/

http://www.alixixi.com/web/a/2014060493448.shtml

http://www.cnblogs.com/haogj/archive/2013/01/15/2861950.html