domReady 理解和自己动手撸一个模型
domReady
在页面的DOM树创建完成后(也就是HTML解析第一步完成)就触发,而不需等待其他资源的加载。也就是DOMReady的实现策略
例如下面这个例子
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script>
document.getElementById("header").style.color="red";
</script>
</head>
<body>
<h1 id="header">这是h1元素包含的内容</h1>
</body>
</html>
直接运行的话,会在控制台报错Uncaught TypeError: Cannot read property 'style' of null
这个就是没有捕获的类型错误,主要是没有获取到header这个元素的错误
这个错误也说明浏览器的机制,是从上往下,从左往右的渲染机制。
在浏览器解析到script标签的时候 body的h1元素还未在页面渲染出来,所以页面提示并没有header这个元素。
这也说明了几个问题
1. 大家一般更习惯把js写在body 的html页面的最底部
2. 日常会使用windwo.onload方法等待页面加载完毕后再进行js
3. 也是我们没有分清html元素和dom节点的问题
HTML标签是要经过浏览器解析才会变成DOM节点
浏览器渲染引擎的渲染流程
- 解析HTML构建DOM树(构建DOM节点)
- 构建渲染树(解析样式信息)
- 布局渲染树(布局DOM节点)
- 绘制渲染树(绘制、遍历DOM节点)
Webkit渲染流程

- HTML标签元素经过HTML解析,依据DOM标准生成DOM树
- CSS样式代码,进过CSS解析,生成网页样式规则
- Webkit使用
Attachment连接DOM节点和可视化信息,构建渲染树 - 渲染树由渲染对象组成,并使用
layout表示元素布局 - 遍历渲染树,在绘制页面
如何得知DOM何时Ready
实际情况:在编写一些复杂程序,大型程序时,js文件很多,之间存在相互引用。 解决思路一(使用延时加载)不推荐
setTimeout(function(){
document.getElementById("header").style.color = "red";
},3000)
加上这个问题是可以解决问题,但是在变色前还等待了一段时间,并没有在domReady的时候开始执行
解决思路二(window.onload)小型项目常用
window.onload = function(){
document.getElementById("header").style.color = "red";
}
本来我觉得这个方法就很稳妥了,但是在咨询了前端大佬后,还是会存在一些问题,通过自己的了解,比如加载一些外部资源还会存在延迟或获取不到的问题等等,其余的一些外部js框架也都会存在 window.onload函数,这样会比较乱。
解决思路三(DOMContentLoaded)推荐
先看看一些优秀的js框架是如何解决的,比如jQuery的解决方法
$(document).ready(function(){
document.getElementById("header").style.color = "red";
});
可以看到他是用了 Jquery.fn.ready 方法里面的jQuery.ready.promise()方法实现的,按照他的实现路线,我们可以照着也来写一个自己实现的。
言归正题,解决思路如下:
1、支持DOMContentLoaded事件的,就使用DOMContentLoaded事件;
2、不支持的(低版本IE)就通过IE中的document.documentElement.doScroll()来判断DOM树是否构建完毕
//接受一个fn,也就是domReady的回调参数
function myReady(fn){
//对于现代浏览器,对DOMContentLoaded事件的处理采用标准的事件绑定方式
// 通过能力检测区分一下浏览器
if ( document.addEventListener ){
document.addEventListener("DOMContentLoaded",fn,false);
} else {
IEContentLoaded(fn);
}
//IE模拟DOMContentLoaded
function IEContentLoaded(fn){
var d = window.document;
var done = false;
// 只执行一次用户的回调函数init()
var init = function () {
if(!done){
done = true;
fn();
}
};
(function (){
try {
//DOM树未创建完之前调用doScroll会抛出错误
d.documentElement.doScroll('left');
} catch (e) {
//延迟在试一次
setTimeout(arguments.callee,50);
return ;
}
//没有错误就表示DOM树创建完毕,然后立马执行用户回调
init();
})();
//监听document的加载状态
d.onreadystatechange = function () {
//如果用户是在domReady之后绑定的函数,就马上执行
if(d.readyState == 'complete'){
d.onreadystatechange = null;
init();
}
}
}
}
在接下来在我们的页面中引入
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="domReady.js"></script>
<script>
myReady(function(){
document.getElementById("header").style.color = "red";
});
</script>
</head>
<body>
<h1 id="header">这是h1元素包含的内容</h1>
</body>
</html>
通过测试,IE、Chrome都可以正常使用了。