• jQuery ready方法实现


    很早之前就留下了这个问题,赶上五一放假,好好研究总结一下吧。

    1. 首先jq中ready方法和window的onload方法的不同这里再提一下,首先ready只是dom树加载完毕,一些img等资源可能还没加载完成,而onload则是全部加载成功。而且ready方法可以有多个,而onload只能写一个。还有一个区别是什么了,想起来补上。

    2. 一般让我们手写模拟一个jq的ready方法,我大多都是这样写:

         document.ready = function (callback) {
                ///兼容FF,Google
                if (document.addEventListener) {
                            document.addEventListener('DOMContentLoaded', function () {
                                document.removeEventListener('DOMContentLoaded', arguments.callee, false);
                                callback();
                            }, false)
                        }
                 //兼容IE
                else if (document.attachEvent) {
                    document.attachEvent('onreadystatechange', function () {
                          if (document.readyState == "complete") {
                                    document.detachEvent("onreadystatechange", arguments.callee);
                                    callback();
                           }
                    })
                }
                else if (document.lastChild == document.body) {
                    callback();
                }
            }
    

    先检测DOMContentLoaded事件,然后撤销绑定,在触发回调,IE的就是检测onreadystatechange事件,如果document.readyState == "complete"就老样子先撤销绑定事件,最后触发回调,最后就是一个退化操作。

    1. ready方法出现的原因就是window.onload事件是在页面所有的资源都加载完毕后触发的. 如果页面上有大图片等资源响应缓慢, 会导致window.onload事件迟迟无法触发.所以出现了DOM Ready事件. 此事件在DOM文档结构准备完毕后触发, 即在资源加载前触发.

    2. 一般在主流浏览器,DOMContentLoaded 事件在许多Webkit浏览器以及IE9上都可以使用, 此事件会在DOM文档准备好以后触发, 包含在HTML5标准中. 对于支持此事件的浏览器, 直接使用DOMContentLoaded事件是最简单最好的选择.但是IE6,7,8都不支持DOMContentLoaded事件。

    这里写图片描述

    在上面代码中也就是这样写的,第一检测的还是DOMContentLoaded事件,而且老版本浏览器或者IE6-8中,hack的方法就不知一种了:

    • 上面也写到的readyState: 如果浏览器存在 document.onreadystatechange 事件,当该事件触发时,如果 document.readyState=complete 的时候,可视为 DOM 树已经载入。
      eg:
        //onreadystatechange event
        document.onreadystatechange = function(e){
            document.getElementById("divMsg").innerHTML += "<br/> onreadystatechange, readyState:" + document.readyState;
         
        };
    
    • doScroll检测:IE浏览器文档中说明,当页面 DOM 未加载完成时,调用 doScroll 方法时,会产生异常。(微软的文档指出doScroll必须在DOM主文档准备完毕时才可以正常触发)那么我们反过来用,如果不异常,那么就是页面DOM加载完毕了,可以不断地通过能否执行 doScroll 判断 DOM 是否加载完毕
        //doScroll
        var doScrollMoniterId = null;
        var doScrollMoniter = function(){
            try{
                document.documentElement.doScroll("left");
                document.getElementById("divMsg").innerHTML += "<br/>doScroll, readyState:" + document.readyState;
                if(doScrollMoniterId){
                    clearInterval(doScrollMoniterId);
                }
            }
            catch(ex){
            }
        }
        doScrollMoniterId = setInterval(doScrollMoniter, 1);
    
    • setTimeout : 在setTimeout中触发的函数, 一定会在DOM准备完毕后触发.
    var setTimeoutReady = function(){
        document.getElementById("divMsg").innerHTML += "<br/> setTimeout , readyState:" + document.readyState;
    };
    var setTimeoutBindReady = function(){
        /in/.test(document.readyState)?setTimeout(arguments.callee, 1):setTimeoutReady();
    };
    setTimeoutBindReady();
    
    • 外部script: 通过设置了script块的defer属性实现.详见:链接
    • 内部script: 外部script的改进版本. 外部script需要页面引用额外的js文件. 内部script方法可以避免此问题。详见:链接

    上面的一些方法其实也存在问题,比如readyState状态为complete的时候图片已经加载完了。
    所以引自一个大佬的说法就是:
    这里写图片描述

    具体使用doScroll的话上次看到了这种写法:

    //为了保证最后一定会调用ready方法,在上面每一种方式的最后都还是会为load事件绑定ready方法。
    if ( document.readyState === "complete" ) {
          // Handle it asynchronously to allow scripts the opportunity to delay ready
          //这里的setTimeout是为了异步
          setTimeout( jQuery.ready, 1 );
    
        // Standards-based browsers support DOMContentLoaded
        //标准浏览器侦听事件接口:document.addEventListener
        } else if ( document.addEventListener ) {
          // Use the handy event callback
          document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
    
          // A fallback to window.onload, that will always work
          //文章一开始说了,这里为了保证一定会触发ready,所以还针对onload也绑定一次回调
          window.addEventListener( "load", jQuery.ready, false );
    
        // If IE event model is used
        } else {
          //IE侦听事件接口:document.attachEvent
          //如果有onreadystatechange事件,侦听之
          // Ensure firing before onload, maybe late but safe also for iframes
          document.attachEvent( "onreadystatechange", DOMContentLoaded );
    
          // A fallback to window.onload, that will always work
          window.attachEvent( "onload", jQuery.ready );
    
    
          // http://javascript.nwbox.com/IEContentLoaded/
          // 见下边说明
          // If IE and not a frame
          // continually check to see if the document is ready
          var top = false;
    
          try {
            top = window.frameElement == null && document.documentElement;
          } catch(e) {}
    
          //如果是IE并且不是iframe
          if ( top && top.doScroll ) {
            (function doScrollCheck() {
              if ( !jQuery.isReady ) {
    
                try {
                  // Use the trick by Diego Perini
                  // http://javascript.nwbox.com/IEContentLoaded/
                  //一直调用doScroll滚动,因为DOM渲染结束前,DOM节点是没有doScroll方法的,所以一直会异常
                  //直到DOM渲染结束了,这个时候doScroll方法不会抛出异常,然后就调用$.ready()
                  top.doScroll("left");
                } catch(e) {
                  return setTimeout( doScrollCheck, 50 );
                }
    
                // and execute any waiting functions
                jQuery.ready();
              }
            })();
          }
        }
    

    上面的代码即实现这样的流程:
    这里写图片描述

    jQuery源码的实现稍有复杂,等日后分析懂了继续补充。

  • 相关阅读:
    Gym
    数学公式头文件
    除法取模(比赛常用)
    ACM-ICPC 2017 Asia Urumqi A. Coins【期望dp】
    P1494 小Z的袜子 【普通莫队】
    Codeforces Round #642 (Div. 3) E—K-periodic Garland dp
    luogu P4568 [JLOI2011]飞行路线 最短路Dijkstra+dp
    luogu P2015 二叉苹果树 树形dp
    luogu P1462 通往奥格瑞玛的道路 二分+spfa
    luogu P1879 [USACO06NOV]Corn Fields G 状态压缩dp
  • 原文地址:https://www.cnblogs.com/zhangmingzhao/p/8972828.html
Copyright © 2020-2023  润新知