专栏首页Czy‘s BlogdomReady的理解

domReady的理解

domReady的理解

domReady是名为DOMContentLoaded事件的别称,当初始的HTML文档被完全加载和解析完成之后,DOMContentLoaded事件被触发,而无需等待样式表、图像和子框架的完全加载。

描述

浏览器渲染DOM结构是有一定顺序的,虽然不同浏览器的实现各有不同,但是基本流程都大致相同:

  • 自上而下,首先解析HTML标签,生成DOM Tree
  • 在解析到<link>或者<style>标签时,开始解析CSS,生成CSSOM,值的注意的是此时解析HTML标签与解析CSS是并行执行的。
  • 当遇到<script>标签后,浏览器会立即开始解析脚本,并停止解析文档,因为脚本有可能会改动DOMCSS,继续解析会浪费资源,所以应当将<script>标签放于<body></body>后。
  • DOM TreeCSSOM生成后,将两者结合进行布局,计算它们的大小位置等布局信息,形成一个能够表示这所有信息的内部表示模型,可称为渲染树render tree
  • 根据计算好的信息绘制整个页面,系统会遍历渲染树,并调用paint方法,将内容显示在屏幕上。

在浏览器解析DOM结构的过程中是存在阻塞过程的:

  • 解析JavaScript过程中会阻塞浏览器的解析过程,准确来说解析渲染过程与解析JavaScript的过程是互斥的。
  • CSS加载解析时不会阻塞DOM树的解析过程,这两个解析过程是可以并行的,但是CSS加载过程中是不能进行JavaScript的解析的,也就是说CSS加载过程中是会阻塞JavaScript的解析,此外因为生成Render Tree时需要CSSOM,所以在DOM Tree解析完成而CSSOM未完成时不会继续生成Render Tree
  • 解析HTML结构同样不会阻塞CSS解析的过程,也同样不会和JavaScript的解析过程并行执行,并且DOM Tree解析未完成而CSSOM完成时同样不会继续生成Render Tree
  • 使用异步加载的<script>标签是不会阻塞DOM解析的,当然其就不会阻塞DOMContentLoaded事件的触发,但是依旧会阻塞load事件的触发。

再来看一下DOMContentLoaded事件与load事件的触发时机:

  • 当初始的HTML文档被完全加载和解析完成之后,DOMContentLoaded事件被触发,而无需等待样式表、图像和子框架的完全加载。关于触发的时机,如果文档中全部为HTMLCSSDomContentLoaded事件无需等到CSS加载完毕即可触发;当Js都在CSS之前DomContentLoaded事件无需等到CSS加载完毕即可触发,当然解析CSSDOM是需要等待前边的Js解析完毕的;当JsCSS之后时,则DomContentLoaded事件需等到CSSJs加载完毕才能够触发,上文也提到了CSS的加载会阻塞Js的加载,而Js标签本身也属于DOM结构,必须等待其加载完成之后才能触发DomContentLoaded事件;异步加载的<script>标签不会阻塞DOMContentLoaded事件。
  • 当整个页面及所有依赖资源如样式表和图片都已完成加载时,将触发load事件。不使用动态加载的<iframe>同样会阻塞load事件,此外即使是异步加载的<script>标签同样会阻塞load事件。

在各种条件下重新整理一下页面加载的过程,主要是在于DOMContentLoaded事件与load事件触发的时间线:

  • 自上而下,首先解析HTML标签,生成DOM Tree,此时document.readyState = "loading"
  • 在解析到<link>或者<style>标签时,开始解析CSS,生成CSSOM,值的注意的是此时解析HTML标签与解析CSS是并行执行的。
  • 解析到没有设置异步加载的<script>的时候,阻塞文档解析,等待Js脚本加载并且执行完成后,才会继续解析文档。
  • 解析到异步<script>的时候不阻塞解析文档,继续向下解析,defer属性会使Js文件等待DOM Tree构建完成之后再执行,而async属性会使Js文件在下载完成后立即执行。
  • 解析文档的时候遇到需要加载外部资源例如图片时,先解析这个节点,根据src创建加载线程,异步加载图片资源,不阻塞解析文档,当然浏览器对于一个域名能够开启最大的线程数量会有限制。
  • 文档解析完成,document.readyState = "interactive"
  • 设置为defer属性的<script>脚本开始按照顺序执行。
  • 触发DOMContentLoaded事件。
  • 等待设置为async属性的<script>以及图片与<iframe>等加载,直至页面完全加载完成。
  • load事件触发,document.readyState = "complete"

调用

有些时候我们希望尽快介入对DOM的干涉,此时调用DOMContentLoaded事件显然更加合适,而为了处理各种浏览器,需要对其进行兼容处理。

  • 对于支持DOMContentLoaded的浏览器使用DOMContentLoaded事件。
  • 如果是小于525版本的Webkit则通过轮询document.readyState来实现。
  • 对于旧版本的IE浏览器使用Diego Perini发现的著名hack
/* https://www.cnblogs.com/JulyZhang/archive/2011/02/12/1952484.html */
/*
 * 注册浏览器的DOMContentLoaded事件
 * @param { Function } onready [必填]在DOMContentLoaded事件触发时需要执行的函数
 * @param { Object } config [可选]配置项
 */
function onDOMContentLoaded(onready, config) {
    //浏览器检测相关对象,在此为节省代码未实现,实际使用时需要实现。
    //var Browser = {};
    //设置是否在FF下使用DOMContentLoaded(在FF2下的特定场景有Bug)
    this.conf = {
        enableMozDOMReady: true
    };
    if (config)
        for (var p in config)
            this.conf[p] = config[p];
    var isReady = false;

    function doReady() {
        if (isReady) return;
        //确保onready只执行一次
        isReady = true;
        onready();
    }
    /*IE*/
    if (Browser.ie) {
        (function() {
            if (isReady) return;
            try {
                document.documentElement.doScroll("left");
            } catch (error) {
                setTimeout(arguments.callee, 0);
                return;
            }
            doReady();
        })();
        window.attachEvent('onload', doReady);
    }
    /*Webkit*/
    else if (Browser.webkit && Browser.version < 525) {
        (function() {
            if (isReady) return;
            if (/loaded|complete/.test(document.readyState))
                doReady();
            else
                setTimeout(arguments.callee, 0);
        })();
        window.addEventListener('load', doReady, false);
    }
    /*FF Opera 高版webkit 其他*/
    else {
        if (!Browser.ff || Browser.version != 2 || this.conf.enableMozDOMReady)
            document.addEventListener("DOMContentLoaded", function() {
                document.removeEventListener("DOMContentLoaded", arguments.callee, false);
                doReady();
            }, false);
        window.addEventListener('load', doReady, false);
    }
}

每日一题

https://github.com/WindrunnerMax/EveryDay

参考

https://juejin.im/post/6844903667733118983
https://juejin.im/post/6844903535314731021
https://juejin.im/post/6844903623583891469
https://juejin.im/post/6844904072340832264
https://juejin.im/post/6844904176569286669
https://www.cnblogs.com/caizhenbo/p/6679478.html
https://www.cnblogs.com/rubylouvre/p/4536334.html
https://developer.mozilla.org/zh-CN/docs/Web/Events/DOMContentLoaded
https://gwjacqueline.github.io/%E5%BC%82%E6%AD%A5%E5%8A%A0%E8%BD%BDjs/

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Js模块化开发的理解

    模块化是一个语言发展的必经之路,其能够帮助开发者拆分和组织代码,随着前端技术的发展,前端编写的代码量也越来越大,就需要对代码有很好的管理,而模块化能够帮助开发者...

    WindrunnerMax
  • 浏览器事件

    常用浏览器事件与DOM事件,包括鼠标事件、键盘事件、框架/对象事件、表单事件、剪贴板事件、打印事件、拖动事件、多媒体事件、动画事件、过渡事件。

    WindrunnerMax
  • CSS引入方式

    将CSS作用到HTML主要有四种方式,分别为HTML元素添加内联样式、<style>标签嵌入样式、<link>标签引入外部样式、@import导入外部样式。

    WindrunnerMax
  • PowerBI 疫情分析 之 全球数据获取

    https://github.com/CSSEGISandData/COVID-19

    BI佐罗
  • 图解Python 3.x多继承时方法解析顺序MRO

    在Python 3.x的多继承树中,如果在中间层某类有向上一层解析的迹象,则会先把本层右侧的其他类方法解析完,然后从本层最后一个解析的类方法中直接进入上一层并继...

    Python小屋屋主
  • 循环神经网络教程第三部分-BPTT和梯度消失

    作者:徐志强 链接:https://zhuanlan.zhihu.com/p/22338087 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,...

    bear_fish
  • 自然梯度优化详解

    对于一阶近似,所有现代的深度学习模型都是使用梯度下降训练的。在梯度下降的每一步,您的参数值开始于某个起点,并将它们移动到最大的损失减少的方向。通过对损失对整个参...

    AiTechYun
  • 炸了!这届ICLR论文被指太“渣”?Goodfellow围追堵截要说法

    安妮 夏乙 发自 凹非寺 量子位 出品 | 公众号 QbitAI 搞机器学习的这帮人在Twitter上又炸了。 起因是一名叫Anish Athalye的小哥率先...

    量子位
  • 巨颖:阅读理解进阶三部曲——关键知识、模型性能提升、产品化落地 | 猿桌会第 61 期

    AI 科技评论按:阅读理解是近两年自然语言处理领域的热点之一,受到学术界和工业界的广泛关注。所谓机器阅读理解,是指让机器通过阅读文本,回答内容相关的问题,其中涉...

    AI科技评论
  • ELK学习笔记之F5 DNS可视化让DNS运维更安全更高效-F5 ELK可视化方案系列(3)

    此文力求比较详细的解释DNS可视化所能带来的场景意义,无论是运维、还是DNS安全。建议仔细看完下图之后的大篇文字段落,希望能引发您的一些思考。

    Jetpropelledsnake21

扫码关注云+社区

领取腾讯云代金券