JavaScript的jQuery库中ready方法的学习教程


Posted in Javascript onAugust 14, 2015

学习 jQuery 有许多途径,我们今天从 jQuery 的 ready 函数开始。本例中的代码都来自于 jQuery 脚本库。

如果你使用过 jQuery , 就必然使用过 ready 函数,它用来注册当页面准备好之后可以执行的函数。

问题来啦,我们的页面什么时候准备好了呢?

onload 事件
最基本的处理方式就是页面的 onload 事件,我们在处理这个事件的时候,可以有多种方式,即可以通过 HTML 方式,直接写在 body 元素的开始标记中,也可以使用事件注册的方式来使用,这又可以分为 DOM0 方式和 DOM2 方式。再考虑到浏览器的兼容性,使用 DOM2 方式写出来,如下所示。

if (document.addEventListener) {
  // A fallback to window.onload, that will always work
  window.addEventListener("load", jQuery.ready, false);

  // If IE event model is used
} else {
  // A fallback to window.onload, that will always work
  window.attachEvent("onload", jQuery.ready);
}

 

DOMContentLoaded 事件
不过 onload 事件要等到所有页面元素加载完成才会触发, 包括页面上的图片等等。如果网页上有大量的图片,效果可想而知,用户可能在没有看到图片的时候,就已经开始操作页面了,而这时我们的页面还没有初始化,事件还没有注册上,这岂不是太晚了!

除了大家熟知的 onload 事件之外, 与 DOM 中的 onload 事件相近的,我们还有 DOMContentLoaded 事件可以考虑, 基于标准的浏览器支持这个事件,  当所有 DOM 解析完以后会触发这个事件。

这样,对于基于标准的浏览器来说,我们还可以注册这个事件的处理。这样,我们可能更早地捕获到加载完成的事件。

if (document.addEventListener) {
  // Use the handy event callback
  document.addEventListener("DOMContentLoaded", DOMContentLoaded, false);

  // A fallback to window.onload, that will always work
  window.addEventListener("load", jQuery.ready, false);
}

onreadystatechange 事件
不标准的浏览器怎么办呢?

如果浏览器存在 document.onreadystatechange 事件,当该事件触发时,如果 document.readyState=complete 的时候,可视为 DOM 树已经载入。

不过,这个事件不太可靠,比如当页面中存在图片的时候,可能反而在 onload 事件之后才能触发,换言之,它只能正确地执行于页面不包含二进制资源或非常少或者被缓存时作为一个备选吧。

if (document.addEventListener) {
  // Use the handy event callback
  document.addEventListener("DOMContentLoaded", DOMContentLoaded, false);

  // A fallback to window.onload, that will always work
  window.addEventListener("load", jQuery.ready, false);

  // If IE event model is used
} else {
  // 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);
}

DOMContentLoaded 函数在做什么呢?最终还是要调用 jQuery.ready 函数。

DOMContentLoaded = function() {
  if ( document.addEventListener ) {
    document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
    jQuery.ready();
  } else if ( document.readyState === "complete" ) {
    // we're here because readyState === "complete" in oldIE
    // which is good enough for us to call the dom ready!
    document.detachEvent( "onreadystatechange", DOMContentLoaded );
    jQuery.ready();
  }
}
doScroll 检测法
MSDN 关于 JScript 的一个方法有段不起眼的话,当页面 DOM 未加载完成时,调用 doScroll 方法时,会产生异常。那么我们反过来用,如果不异常,那么就是页面DOM加载完毕了!

Diego Perini 在 2007 年的时候,报告了一种检测 IE 是否加载完成的方式,使用 doScroll 方法调用。详细的说明见这里。
原理是对于 IE 在非 iframe 内时,只有不断地通过能否执行 doScroll 判断 DOM 是否加载完毕。在本例中每间隔 50 毫秒尝试去执行 doScroll,注意,由于页面没有加载完成的时候,调用 doScroll 会导致异常,所以使用了 try -catch 来捕获异常。

(function doScrollCheck() {
  if (!jQuery.isReady) {

    try {
      // Use the trick by Diego Perini
      // http://javascript.nwbox.com/IEContentLoaded/
      top.doScroll("left");
    } catch (e) {
      return setTimeout(doScrollCheck, 50);
    }

    // and execute any waiting functions
    jQuery.ready();
  }
})();

document.readyState 状态
如果我们注册 ready 函数的时间点太晚了,页面已经加载完成之后,我们才注册自己的 ready 函数,那就用不着上面的层层检查了,直接看看当前页面的 readyState 就可以了,如果已经是 complete ,那就可以直接执行我们准备注册的 ready 函数了。不过 ChrisS 报告了一个很特别的错误情况,我们需要延迟一下执行。

setTimeout 经常被用来做网页上的定时器,允许为它指定一个毫秒数作为间隔执行的时间。当被启动的程序需要在非常短的时间内运行,我们就会给她指定一个很小的时间数,或者需要马上执行的话,我们甚至把这个毫秒数设置为0,但事实上,setTimeout有一个最小执行时间,当指定的时间小于该时间时,浏览器会用最小允许的时间作为setTimeout的时间间隔,也就是说即使我们把setTimeout的毫秒数设置为0,被调用的程序也没有马上启动。

这个最小的时间间隔是多少呢?这和浏览器及操作系统有关。在John Resig的新书《Javascript忍者的秘密》一书中提到

    Browsers all have a 10ms minimum delay on OSX and a(approximately) 15ms delay on Windows.(在苹果机上的最小时间间隔是10毫秒,在Windows系统上的最小时间间隔大约是15毫秒)

,另外,MDC中关于setTimeout的介绍中也提到,Firefox中定义的最小时间间隔(DOM_MIN_TIMEOUT_VALUE)是10毫秒,HTML5定义的最小时间间隔是4毫秒。既然规范都是这样写的,那看来使用setTimeout是没办法再把这个最小时间间隔缩短了。

这样,通过设置为 1, 我们可以让程序在浏览器支持的最小时间间隔之后执行了。

// 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") {
  // 延迟 1 毫秒之后,执行 ready 函数
  setTimeout(jQuery.ready, 1);
}

 

完整的代码
在 jQuery 中完整的代码如下所示。位于 jQuery 1.8.3 源代码的 #842 行。

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

    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( jQuery.ready, 1 );

    // Standards-based browsers support DOMContentLoaded
    } else if ( document.addEventListener ) {
      // Use the handy event callback
      document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );

      // A fallback to window.onload, that will always work
      window.addEventListener( "load", jQuery.ready, false );

    // If IE event model is used
    } else {
      // 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 );

      // 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) {}

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

            try {
              // Use the trick by Diego Perini
              // http://javascript.nwbox.com/IEContentLoaded/
              top.doScroll("left");
            } catch(e) {
              return setTimeout( doScrollCheck, 50 );
            }

            // and execute any waiting functions
            jQuery.ready();
          }
        })();
      }
    }
  }
  return readyList.promise( obj );
};

那么,又是谁来调用呢?当然是需要的时候,在我们调用 ready 函数的时候,才需要注册这些判断页面是否完全加载的处理,这段代码在 1.8.3 中位于代码的 #244 行,如下所示:

ready: function( fn ) {
  // Add the callback
  jQuery.ready.promise().done( fn );

  return this;
}

在页面上引用 jQuery 脚本库之后,执行了 jQuery 的初始化函数,初始化函数中创建了 ready 函数。我们在通过 ready 函数注册事件处理之前,jQuery 完成了页面检测代码的注册。这样。当页面完全加载之后,我们注册的函数就被调用了。

jQuery Ready 方法的简短写法

写 jQuery 代码的时候,一般要写一个 Ready 方法,以确保 DOM 已加载完毕,然后再执行相应的 jQuery 代码。Ready 方法一般写法如下:

$(document).ready(function() {
  // 从这里开始
});

但是在看其他人写的 jQuery 代码的时候,经常又会看到如下写法:

$(function() {
  // 从这里开始
});

第二种写法虽然简短了许多,但是在功能上和第一种写法是等价的,如果你不相信,可以看一下 jQuery 的源代码中有如下代码片段:

// HANDLE: $(function)
// Shortcut for document ready 
if ( jQuery.isFunction( selector ) ) {
  return rootjQuery.ready( selector );
}

如果传入选择器中的参数是一个函数,那么会自动返回一个 rootjQuery.ready( selector ),而 rootjQuery 又是 jQuery(document) 的一个引用,所以这里就相当于调用 jQuery(document).ready() 方法,而之前的那个匿名方法亦被传入其中以备执行。

这种简短写法虽说减少了了一点代码量,但是可读性稍差,所以我个人还是倾向于前面的第一种写法,特别是在团队开发中,仅仅是为了语意明确。

 

Javascript 相关文章推荐
通过JS自动隐藏手机浏览器的地址栏实现原理与代码
Jan 02 Javascript
JS给Textarea文本框添加行号的方法
Aug 20 Javascript
利用jQuery实现WordPress中@的ID悬浮显示评论内容
Dec 11 Javascript
JS实现动态表格的添加,修改,删除功能(推荐)
Jun 15 Javascript
javascript 面向对象function详解及实例代码
Feb 28 Javascript
详解Angular结合zTree异步加载节点数据
Jan 20 Javascript
全站最详细的Vuex教程
Apr 13 Javascript
jQuery插件Validation表单验证详解
May 26 jQuery
Vue.js实现双向数据绑定方法(表单自动赋值、表单自动取值)
Aug 27 Javascript
js变量声明var使用与不使用的区别详解
Jan 21 Javascript
JS实现旋转木马轮播图
Jan 01 Javascript
javascript局部自定义鼠标右键菜单
Dec 08 Javascript
js实现当复选框选择匿名登录时隐藏登录框效果
Aug 14 #Javascript
JavaScript学习笔记之DOM基础 2.4
Aug 14 #Javascript
关于JavaScript的变量的数据类型的判断方法
Aug 14 #Javascript
js实现表单检测及表单提示的方法
Aug 14 #Javascript
JavaScript中的this关键字使用详解
Aug 14 #Javascript
JS实现双击编辑可修改状态的方法
Aug 14 #Javascript
JavaScript变量的作用域全解析
Aug 14 #Javascript
You might like
PHP 防恶意刷新实现代码
2010/05/16 PHP
php自动获取目录下的模板的代码
2010/08/08 PHP
UCenter 批量添加用户的php代码
2012/07/17 PHP
跟我学Laravel之快速入门
2014/10/15 PHP
php实现 master-worker 守护多进程模式的实例代码
2019/07/20 PHP
javascript判断iphone/android手机横竖屏模式的函数
2011/12/20 Javascript
js禁止document element对象选中文本实现代码
2013/03/21 Javascript
jquery序列化form表单使用ajax提交后处理返回的json数据
2014/03/03 Javascript
删除条目时弹出的确认对话框
2014/06/05 Javascript
JS实现table表格数据排序功能(可支持动态数据+分页效果)
2016/05/26 Javascript
AngularJS 中的数据源的循环输出
2017/10/12 Javascript
node 命令方式启动修改端口的方法
2018/05/12 Javascript
如何使用electron-builder及electron-updater给项目配置自动更新
2018/12/24 Javascript
vue实现按需加载组件及异步组件功能
2019/05/27 Javascript
Python linecache.getline()读取文件中特定一行的脚本
2008/09/06 Python
python生成器的使用方法
2013/11/21 Python
零基础写python爬虫之爬虫框架Scrapy安装配置
2014/11/06 Python
Python算法应用实战之队列详解
2017/02/04 Python
Python实现中文数字转换为阿拉伯数字的方法示例
2017/05/26 Python
Python之列表实现栈的工作功能
2019/01/28 Python
Python元组知识点总结
2019/02/18 Python
使用 Python 玩转 GitHub 的贡献板(推荐)
2019/04/04 Python
python快速编写单行注释多行注释的方法
2019/07/31 Python
python suds访问webservice服务实现
2020/06/26 Python
CSS中越界问题的经典解决方案【推荐】
2016/04/19 HTML / CSS
HTML4和HTML5之间除了相似以外的10个主要不同
2012/12/13 HTML / CSS
使用phonegap进行提示操作的具体方法
2017/03/30 HTML / CSS
英国高端食品和葡萄酒超市:Waitrose
2016/08/23 全球购物
中国高端鲜花第一品牌:roseonly(一生只送一人)
2017/02/12 全球购物
英国领先的新鲜松露和最好的松露产品供应商:TruffleHunter
2019/08/26 全球购物
牵手50新加坡:专为黄金岁月的单身人士而设的交友网站
2020/08/16 全球购物
平面设计师工作职责范文
2013/12/03 职场文书
计算机专业毕业生求职信
2014/04/30 职场文书
2015年项目工作总结
2015/04/29 职场文书
2016医师资格考试考生诚信考试承诺书
2016/03/25 职场文书
Centos环境下Postgresql 安装配置及环境变量配置技巧
2021/05/18 PostgreSQL