[转载]document.readyState

原文:document.readyState

document.onreadystatechange

DOMContentLoaded

使用document.readyStatedocument.onreadystatechange搭配可以模拟出jquery ready和window.onload的效果。如果页面上有大图片等资源响应缓慢, 会导致window.onload事件迟迟无法触发.所以出现了DOM Ready事件. 此事件在DOM文档结构准备完毕后触发, 即在资源加载前触发.

1. document.readyState

1.1. 概述

一个document 的 Document.readyState 属性描述了文档的加载状态。

1.2. 值

一个文档的 readyState 可以是以下之一:

  • loading / 加载 。document 仍在加载
  • interactive / 互动。文档已经完成加载,文档已被解析,但是诸如图像,样式表和框架之类的子资源仍在加载
  • complete / 完成。T文档和所有子资源已完成加载。状态表示 load 事件即将被触发

当这个属性的值变化时,document 对象上的readystatechange 事件将被触发。

1.3. 语法

let string = document.readyState;

// "complete"

1.4. 例子

不同的准备状态

switch (document.readyState) {
  case "loading":
    // The document is still loading.
    break;
  case "interactive":
    // The document has finished loading.
    // We can now access the DOM elements.
    var span = document.createElement("span");
    span.textContent = "A <span> element.";
    document.body.appendChild(span);
    break;
  case "complete":
    // The page is fully loaded.
    let CSS_rule = document.styleSheets[0].cssRules[0].cssText;
    console.log(`The first CSS rule is: ${CSS_rule }`);
    break;
}
// 模拟 DOMContentLoaded/ jquery ready
document.onreadystatechange = function () {
  if (document.readyState === "interactive") {
    initApplication();
  }
}
// 模拟 load/onload 事件
document.onreadystatechange = function () {
  if (document.readyState === "complete") {
    initApplication();
  }
}

2. document.onreadystatechange

2.1. 概述

当一个文档的 readyState 属性发生更改时,readystatechange 事件会被触发。

2.2. 语法

document.onreadystatechange = funcRef;

funcRef 是个函数引用,会在readystatechange事件触发时调用.

2.3. 例子

/*
interactive / 互动
文档已经完成加载,文档已被解析,但是诸如图像,样式表和框架之类的子资源仍在加载。
https://developer.mozilla.org/zh-CN/docs/Web/API/Document/readyState
​​​​​​​*/

// 模拟DOMContentLoaded事件

document.onreadystatechange = function () {
    if (document.readyState === "interactive") {
        initApplication();
    }
}

3. DOMContentLoaded

DOMContentLoaded在ie8及以下不支持。

DOMContentLoaded在jquery中有使用。

当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像和子框架的完成加载。另一个不同的事件 load 应该仅用于检测一个完全加载的页面。 在使用 DOMContentLoaded 更加合适的情况下使用 load 是一个令人难以置信的流行的错误,所以要谨慎。注意:DOMContentLoaded 事件必须等待其所属script之前的样式表加载解析完成才会触发。

模拟的css文件:CSS.php

<?php
sleep(3);

测试代码:

<link rel="stylesheet" href="css.php">
<script>
document.addEventListener('DOMContentLoaded',function(){
    console.log('3 seconds passed');
});
</script>

如果将link置于script之后,就会立即打印。

Note: 同步 JavaScript 会暂停 DOM 的解析。
Note: 还有许多通用和独立的库提供跨浏览器方法来检测 DOM 是否已准备就绪

3.1. 加速中

如果您希望 DOM 在用户请求页面后尽可能快地解析,你可以做的一些事情是把你的 JavaScript 异步化 以及 优化样式表的加载, 由于被并行加载而减慢页面加载,从主 html 文档“窃取”流量。

3.2. 示例

<script>
  document.addEventListener("DOMContentLoaded", function(event) {
      console.log("DOM fully loaded and parsed");
  });
</script>
<script>
  document.addEventListener("DOMContentLoaded", function(event) {
      console.log("DOM fully loaded and parsed");
  });

  for(var i=0; i<1000000000; i++){
      // 这个同步脚本将延迟DOM的解析。
      // 所以DOMContentLoaded事件稍后将启动。
  } 
</script>

4. jquey.ready实现

参考:jQuery deferred.resolveWith() 方法

jQuery.holdReady()方法

4.1. 定义readyList等标识

  • readyList:待执行的回调函数队列。
  • isReady:DOM ready 是否已经完成标识
  • readyWait:
  • holdReady:jQuery.holdReady()方法
var
    // The deferred used on DOM ready
    // 一个用在 DOM ready 上的回调函数处理变量
    readyList,
    
    // Is the DOM ready to be used? Set to true once it occurs.
    // DOM ready 是否已经完成
    isReady: false,

    // A counter to track how many items to wait for before
    // the ready event fires. See #6781
    // 控制有多少个 holdReady 事件需要在 Dom ready 之前执行
    readyWait: 1,
    
    // Hold (or release) the ready event
    // 方法允许调用者延迟 jQuery 的 ready 事件
    // example. 延迟就绪事件,直到已加载的插件。
    //
    // $.holdReady(true);
    // $.getScript("myplugin.js", function() {
    //   $.holdReady(false);
    // });
    //
    holdReady: function(hold) {
        if (hold) {
            jQuery.readyWait++;
        } else {
            jQuery.ready(true);
        }
    },

4.2. ready定义

// Handle when the DOM is ready
ready: function(wait) {
    // Abort if there are pending holds or we're already ready
    // 如果需要等待,holdReady()的时候,把hold住的次数减1,如果还没到达0,说明还需要继续hold住,return掉
    // 如果不需要等待,判断是否已经Ready过了,如果已经ready过了,就不需要处理了。异步队列里边的done的回调都会执行了
    if (wait === true ? --jQuery.readyWait : jQuery.isReady) {
        return;
    }

    // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
    // 确定 body 存在
    if (!document.body) {
        // 如果 body 还不存在 ,DOMContentLoaded 未完成,此时
        // 将 jQuery.ready 放入定时器 setTimeout 中
        // 不带时间参数的 setTimeout(a) 相当于 setTimeout(a,0)
        // 但是这里并不是立即触发 jQuery.ready
        // 由于 javascript 的单线程的异步模式
        // setTimeout(jQuery.ready) 会等到重绘完成才执行代码,也就是 DOMContentLoaded 之后才执行 jQuery.ready
        // 所以这里有个小技巧:在 setTimeout 中触发的函数, 一定会在 DOM 准备完毕后触发
        return setTimeout(jQuery.ready);
    }

    // Remember that the DOM is ready
    // 记录 DOM ready 已经完成
    jQuery.isReady = true;

    // If a normal DOM Ready event fired, decrement, and wait if need be
    // wait 为 false 表示ready事情未触发过,否则 return
    if (wait !== true && --jQuery.readyWait > 0) {
        return;
    }

    // If there are functions bound, to execute
    // 调用异步队列,然后派发成功事件出去(最后使用done接收,把上下文切换成document,默认第一个参数是jQuery。
    readyList.resolveWith(document, [jQuery]);

    // Trigger any bound ready events
    // 最后jQuery还可以触发自己的ready事件
    // 例如:
    //    $(document).on('ready', fn2);
    //    $(document).ready(fn1);
    // 这里的fn1会先执行,自己的ready事件绑定的fn2回调后执行
    if (jQuery.fn.trigger) {
        jQuery(document).trigger("ready").off("ready");
    }
},

4.3. 执行

其实逻辑不是很复杂,只是为了兼容不同的浏览器。

// $.ready()
jQuery.ready.promise = function(obj) {
    if (!readyList) {

        // 如果没有,新建一个 Deferred 对象
        // Deferred 用于处理异步延时回调函数,也就是内部用于 ready 的一个异步队列
        readyList = jQuery.Deferred();

        // Catch cases where $(document).ready() is called after the browser event has already occurred.
        // we once tried to use readyState "interactive" here, but it caused issues like the one
        // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
        if (document.readyState === "complete") {
            // Handle it asynchronously to allow scripts the opportunity to delay ready
            // setTimeout : 在setTimeout中触发的函数, 一定会在DOM准备完毕后触发.(即是 DOMContentLoaded)
            setTimeout(jQuery.ready);

            // Standards-based browsers support DOMContentLoaded
            // 支持 DOMContentLoaded 的浏览器 (除去ie 6 7 8)
        } else if (document.addEventListener) {
            // Use the handy event callback
            // 当检测的 document.readyState 的值不为 complete 时, 用 readystatechange 监听 document.readyState 值的变化事件
            document.addEventListener("DOMContentLoaded", completed, false);

            // A fallback to window.onload, that will always work
            // 一种退而求其次的方法,确保一定会发生
            window.addEventListener("load", completed, false);

            // If IE event model is used
            // 如果是 IE 浏览器(6、7、8)
        } else {
            // Ensure firing before onload, maybe late but safe also for iframes
            document.attachEvent("onreadystatechange", completed);

            // A fallback to window.onload, that will always work
            window.attachEvent("onload", completed);

            // If IE and not a frame
            // continually check to see if the document is ready
            // 如果是 IE 且不是在 frame 中
            var top = false;

            try {
                top = window.frameElement == null && document.documentElement;
            } catch (e) {}

            // 如果是IE并且不是iframe
            if (top && top.doScroll) {
                // 这里有个立即执行函数 doScrollCheck()
                (function doScrollCheck() {
                    if (!jQuery.isReady) {

                        try {
                            // Use the trick by Diego Perini
                            // http://javascript.nwbox.com/IEContentLoaded/
                            // Diego Perini 在 2007 年的时候,报告了一种检测 IE 是否加载完成的方式,使用 doScroll 方法调用
                            // 原理就是对于 IE 在非 iframe 内时,只有不断地通过能否执行 doScroll 判断 DOM 是否加载完毕
                            // 在上述中间隔 50 毫秒尝试去执行 doScroll,注意,由于页面没有加载完成的时候,调用 doScroll 会导致异常,所以使用了 try - catch 来捕获异常
                            // 直到DOM渲染结束了,这个时候 doScroll 方法不会抛出异常,然后就调用$.ready()
                            top.doScroll("left");
                        } catch (e) {
                            return setTimeout(doScrollCheck, 50);
                        }

                        // detach all dom ready events
                        detach();

                        // and execute any waiting functions
                        jQuery.ready();
                    }
                })();
            }
        }
    }
    // 函数返回的是deferred对象,这就可以加上链式操作了
    // 可以使用 .done .fail 等方法
    return readyList.promise(obj);

    // Populate the class2type map
};

// The ready event handler
completed = function(event) {

    // readyState === "complete" is good enough for us to call the dom ready in oldIE
    if (document.addEventListener || event.type === "load" || document.readyState === "complete") {
        detach();
        jQuery.ready();
    }
},
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值