zoukankan      html  css  js  c++  java
  • 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/
    
  • 相关阅读:
    [模板] 循环数组的最大子段和
    [最短路][几何][牛客] [国庆集训派对1]-L-New Game
    [洛谷] P1866 编号
    1115 Counting Nodes in a BST (30 分)
    1106 Lowest Price in Supply Chain (25 分)
    1094 The Largest Generation (25 分)
    1090 Highest Price in Supply Chain (25 分)
    树的遍历
    1086 Tree Traversals Again (25 分)
    1079 Total Sales of Supply Chain (25 分 树
  • 原文地址:https://www.cnblogs.com/WindrunnerMax/p/13706499.html
Copyright © 2011-2022 走看看