jQuery中的ready

基于jQuery v1.8.3

在js与DOM交互之前要确保DOM已经加载构建完成,在jQuery中都是使用 (fn) (document).ready(fn)来确保自己写的代码在DOM构建完成之后执行。
那么jQuery的ready事件内部怎么实现的呢?

通过阅读源码(line:842 ~ 898)知道jQuery内部使用四种判别文档是否加载完成的事件:

  1. DOMContentLoaded 初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像和子框架的完成加载

  2. load 一个页面完全加载之后触发

  3. readyStateChange 文档加载状态(document.readyState)改变的时候触发,当document.readyState === ‘complete’ 为 true 表示文档解析完成。

    readyState可能的值有:

    1. loading 加载
    
    2. interactive 互动
    
    3. complete 完成
    
  4. doScroll 低版本IE支持的事件,如果调用不出错就表示文档解析完成

部分源码如下:

/**
*先检查一下文档是否加载完成了,如果执行这个方法的时候文档已经加载完成还不知道就尴尬了。。。后面注册的监听文档是否加载完成的事件都不会被触发
*因为我们都知道事件的特性就是错过了就是错过了
*/
if ( document.readyState === "complete" ) {
  setTimeout( jQuery.ready, 1 );
} else if ( document.addEventListener ) {
  // 检测是否支持 DOMContentLoaded,该事件这是H5新加的事件,在DOM结构构件完成触发,不会等待资源的下载如IMG等,所以会比window.onload提前触发
  document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
  // 监听onload事件
  window.addEventListener( "load", jQuery.ready, false );
} else {
  // 这里是低版本的IE浏览器
  // 通过attachEvent监听readystatechange和load事件
  document.attachEvent( "onreadystatechange", DOMContentLoaded );

  window.attachEvent( "onload", jQuery.ready );

  // 在顶层不在iframe里面
  var top = false;

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

  if ( top && top.doScroll ) {
    (function doScrollCheck() {
      if ( !jQuery.isReady ) {

        try {
          top.doScroll("left");
        } catch(e) {
          return setTimeout( doScrollCheck, 50 );
        }

        jQuery.ready();
      }
    })();
  }

通过以上源码分析,自己来实现一个完整的

需求:

确保代码会在文档加载完的第一时间被调用

可以正确处理多次调用(调用多次ready(fn); ready(fn))

(function(window, undefined){
  // 任务队列
  var PENDING = 'pending',
      COMPLETE = 'complete',
      first = true, // 第一次调用ready方法
      queue = [],
      state = PENDING; // pending complate

  var obser = {
    queue: [],
    fire: function(){
      while(this.queue.length){
        this.queue.shift()();
      }
    },
    add: function(fn){
      this.queue.push(fn);
    }
  };

  function loaded(){
    // 防止被触发多次
    if(state === COMPLETE) return;

    if(document.addEventListener){
      state = COMPLETE;
      obser.fire();
      document.removeEventListener('DOMContentLoaded', loaded, false);
      window.removeEventListener('load', loaded, false);
    }else if(document.readyState === 'complete'){
      // 这里必须要是 document.readyState === 'complate' 才行
      state = COMPLETE;
      obser.fire();
      document.detachEvent('DOMContentLoaded', loaded);
      window.detachEvent('load', loaded);
    }
  }

  function ready(fn){
    if(first){
      // 如果是第一次调用顺待要注册一下事件
      if(document.readyState === 'complete'){
        loaded();
      }else if(document.addEventListener){
        document.addEventListener('DOMContentLoaded', loaded, false);
        window.addEventListener('load', loaded, false);
      }else{
        // 这里处理IE
        document.attachEvent('onreadystatechange', loaded);
        window.attachEvent('onload', loaded);
      }
    }

    if(state === COMPLETE){
      fn();
    }else{
      obser.add(fn);
    }
  }

  window.ready = ready;
})(window, undefined);

// 之后直接调用ready(fn)即可

猜你喜欢

转载自blog.csdn.net/lettertiger/article/details/79989852