正在进入ing...

domReady 理解和自己动手撸一个模型

发布时间:2020-09-08 浏览量: 1782 文章分类: 前端相关

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标签的时候 bodyh1元素还未在页面渲染出来,所以页面提示并没有header这个元素。 这也说明了几个问题 1. 大家一般更习惯把js写在body 的html页面的最底部 2. 日常会使用windwo.onload方法等待页面加载完毕后再进行js 3. 也是我们没有分清html元素和dom节点的问题 HTML标签是要经过浏览器解析才会变成DOM节点

浏览器渲染引擎的渲染流程

  • 解析HTML构建DOM树(构建DOM节点)
  • 构建渲染树(解析样式信息)
  • 布局渲染树(布局DOM节点)
  • 绘制渲染树(绘制、遍历DOM节点)

Webkit渲染流程

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>

通过测试,IEChrome都可以正常使用了。