一直以来,各种JS最佳实践都会告诉我们,将JS放在HTML的最后,即</body>之前,理由就是:JS会阻塞下载,而且,在JS中很有可能有对DOM的操作,放在HTML的最后,可以尽可能的保证JS的执行在DOM加载完成之后。而如果放在onload事件中执行,如果页面有很多图像,那么页面的onload事件要过很久才会触发,因此DOM Ready事件就是最好的执行JS的时间了。

所以,如果有个DOM Ready事件就好了,虽然现代浏览器已经支持DOMContentLoaded事件,但是我们还是得处理那些老旧的浏览器,于是DOM Ready事件就成了各个JS框架的必备功能啦。

先来看看jQuery DOM Ready事件的用法吧,jQuery的DOM Ready事件用法很简单,大家都用过,下面的三种语法都是可用的:

  • $( document ).ready( handler )
  • $().ready( handler )(不推荐)
  • $( handler )

还有一种方式:$(document).on('ready', handler);,已经在1.8版本里被废弃了,但是你还可以这么用。这种方式和ready方法的作用一样,但是如果ready事件已经被触发过了,这种方式绑定的handler将不会被执行,而且用这种方式绑定的handler会在以上三种方法绑定的handler被执行后再执行。

一、DOM Ready检测

下面,我们按照jQuery的思路,一步步的看一下jQuery如何处理DOM Ready事件的。

1. 标准浏览器

对于标准浏览器,我们可以添加DOMContentLoaded事件监听器:

  1. if ( document.addEventListener ) {
  2. document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
  3. }
2. 非标准浏览器

而对于非标准浏览器,我们可以监听document.onreadystatechange事件,如果document.readyState == ‘complete’时,可以认为DOM结构已经加载完成。

  1. if ( document.addEventListener ) {
  2. document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
  3.  
  4. // If IE event model is used
  5. } else {
  6. document.attachEvent( "onreadystatechange", DOMContentLoaded );
  7. }

这里不用担心jQuery.ready()会被多次触发,因为在jQuery.ready()中有检测是否被触发过了。

4. 还是IE

在IE中,onreadystatechange事件对于iframe的来说可能会有延迟,但是却足够安全。但这个事件对于非iframe有时会不太可靠,特别是在页面中图片资源比较多的时候,可能反而在onload事件触发之后才能触发,因此对于这种情况,需要做进一步的检测:

  1. if ( document.addEventListener ) {
  2. document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
  3.  
  4. // A fallback to window.onload, that will always work
  5. window.addEventListener( "load", jQuery.ready, false );
  6.  
  7. // If IE event model is used
  8. } else {
  9. document.attachEvent( "onreadystatechange", DOMContentLoaded );
  10.  
  11. // A fallback to window.onload, that will always work
  12. window.attachEvent( "onload", jQuery.ready );
  13.  
  14. // If IE and not a frame
  15. // continually check to see if the document is ready
  16. var top = false;
  17.  
  18. try {
  19. top = window.frameElement == null && document.documentElement;
  20. } catch(e) {}
  21.  
  22. // 如果是非iframe的情况,并且支持doScroll方法
  23. if ( top && top.doScroll ) {
  24. // 在这个自执行函数中,调用top.doScroll方法,若报错,则延时50ms再检测;若不报错,则表示DOM Ready,可以执行jQuery.ready()方法了
  25. (function doScrollCheck() {
  26. if ( !jQuery.isReady ) {
  27. try {
  28. top.doScroll("left");
  29. } catch(e) {
  30. return setTimeout( doScrollCheck, 50 );
  31. }
  32. // and execute any waiting functions
  33. jQuery.ready();
  34. }
  35. })();
  36. }
  37. }

是的,JS就是这么一门神(dan)奇(teng)的语言,每个浏览器的实现可能不尽相同,所以就出现了这种对一个事件的监听要做如此多的兼容考虑的情况。

5. Ready太晚了

还有一种情况是,当我们调用$(document).ready()的时候,DOM结构已经加载完成了,这时候可以直接调用jQuery.ready,但是因为IE的问题,需要延迟调用,所以有下面的代码,至于为什么要这么用,小Y也没看明白:

  1. if ( document.readyState === "complete" ) {
  2. // Handle it asynchronously to allow scripts the opportunity to delay ready
  3. setTimeout( jQuery.ready, 1 );
  4. } else if ( document.addEventListener ) {
  5. document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
  6.  
  7. // A fallback to window.onload, that will always work
  8. window.addEventListener( "load", jQuery.ready, false );
  9.  
  10. // If IE event model is used
  11. } else {
  12. document.attachEvent( "onreadystatechange", DOMContentLoaded );
  13.  
  14. // A fallback to window.onload, that will always work
  15. window.attachEvent( "onload", jQuery.ready );
  16.  
  17. // If IE and not a frame
  18. // continually check to see if the document is ready
  19. var top = false;
  20.  
  21. try {
  22. top = window.frameElement == null && document.documentElement;
  23. } catch(e) {}
  24.  
  25. // 如果是非iframe的情况,并且支持doScroll方法
  26. if ( top && top.doScroll ) {
  27. // 在这个自执行函数中,调用top.doScroll方法,若报错,则延时50ms再检测;若不报错,则表示DOM Ready,可以执行jQuery.ready()方法了
  28. (function doScrollCheck() {
  29. if ( !jQuery.isReady ) {
  30. try {
  31. top.doScroll("left");
  32. } catch(e) {
  33. return setTimeout( doScrollCheck, 50 );
  34. }
  35. // and execute any waiting functions
  36. jQuery.ready();
  37. }
  38. })();
  39. }
  40. }

到这里,jQuery对DOM Ready的检测就完成了,那么$(document).ready()是怎么工作的呢?

二、DOM Ready全过程

这里,我们按代码执行的顺序,来理解jQuery的DOM Ready的全部代码,看jQuery是如何一步步处理DOM Ready事件的。为了阅读的方便,小Y将代码做了删减,并对代码顺序做了调整,大家可以按从1到7的顺序看代码。这里不对Deferred对象做解释,因为小Y也还没有看,只是记住他的作用是维护一系列条件触发的回调函数。

  1. jQuery.fn = jQuery.prototype = {
  2. init: function( selector, context, rootjQuery ) {
  3. if ( jQuery.isFunction( selector ) ) {
  4. // 当调用$(callback); 时,会调用$(document).ready(callback); Ⅰ
  5. return rootjQuery.ready( selector );
  6. }
  7. },
  8.  
  9. ready: function( fn ) {
  10. // Add the callback
  11. // 调用jQuery.ready.promise方法,返回一个Deferred对象 readyList,
  12. // 然后将fn加入到readyList的成功的回调列表中。
  13. // 如果readyList已存在,则直接返回readyList,然后直接调用这个回调函数fn
  14. jQuery.ready.promise()
  15. .done( fn );
  16.  
  17. return this;
  18. }
  19. };
  20.  
  21. jQuery.ready.promise = function( obj ) {
  22. if ( !readyList ) {
  23.  
  24. readyList = jQuery.Deferred();
  25.  
  26. // IE的bug
  27. if ( document.readyState === "complete" ) {
  28. setTimeout( jQuery.ready, 1 );
  29.  
  30. // 给标准浏览器绑定DOMContentLoaded事件以触发DOMContentLoaded方法
  31. // 同时,绑定window.onload事件作为备用方案
  32. } else if ( document.addEventListener ) {
  33. document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
  34. window.addEventListener( "load", jQuery.ready, false );
  35.  
  36. // 给IE浏览器绑定onreadystatechange事件,在DOMContentLoaded中判断
  37. // readyState是否为complete
  38. } else {
  39. document.attachEvent( "onreadystatechange", DOMContentLoaded );
  40. window.attachEvent( "onload", jQuery.ready );
  41.  
  42. // 对IE做进一步的兼容性检测
  43. var top = false;
  44. try {
  45. top = window.frameElement == null && document.documentElement;
  46. } catch(e) {}
  47. if ( top && top.doScroll ) {
  48. (function doScrollCheck() {
  49. if ( !jQuery.isReady ) {
  50. try {
  51. // Use the trick by Diego Perini
  52. // http://javascript.nwbox.com/IEContentLoaded/
  53. top.doScroll("left");
  54. } catch(e) {
  55. return setTimeout( doScrollCheck, 50 );
  56. }
  57. // and execute any waiting functions
  58. jQuery.ready();
  59. }
  60. })();
  61. }
  62. }
  63. }
  64. // 返回readyList
  65. return readyList.promise( obj );
  66. };
  67.  
  68. // 接下来就是等待DOMContentLoaded/readyState==='complete'事件的发生了
  69. // 移除DOMReady事件监听,执行jQuery.ready()
  70. DOMContentLoaded = function() {
  71. // 标准浏览器
  72. if ( document.addEventListener ) {
  73. document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
  74. jQuery.ready();
  75. // IE 浏览器
  76. } else if ( document.readyState === "complete" ) {
  77. document.detachEvent( "onreadystatechange", DOMContentLoaded );
  78. jQuery.ready();
  79. }
  80. };
  81.  
  82. jQuery.extend({
  83. // DOM Ready标志位,DOM Ready触发后置为true
  84. isReady: false,
  85.  
  86. // DOM Ready后运行
  87. ready: function( ) {
  88.  
  89. // 如果已经Ready,则直接返回
  90. if ( jQuery.isReady ) {
  91. return;
  92. }
  93.  
  94. // 兼容IE
  95. if ( !document.body ) {
  96. return setTimeout( jQuery.ready, 1 );
  97. }
  98.  
  99. // 将isReady置为true
  100. jQuery.isReady = true;
  101.  
  102. // 这里是真正触发我们传入的回调的地方!!!!
  103. // 传入上下文和回调函数的参数列表
  104. // 注意啦,上下文是document,也就是说我们在`$(document).ready(fn);`里的fn中的this是`document`,不是`window`
  105. // 参数列表传入了jQuery,意味着即使我们调用了`$.noConflict()`将$让渡出去了,我们仍然能在fn中定义第一个参数为$,代表jQuery
  106. readyList.resolveWith( document, [ jQuery ] );
  107.  
  108. // 这段代码是为了触发这种方式添加的DOM Ready回调: $(document).on('ready' , fn);
  109. // 这种方式在1.8被废弃,但是没有被移除
  110. // 你可以看到这种方式添加的DOM Ready回调是最后被执行的
  111. if ( jQuery.fn.trigger ) {
  112. jQuery( document ).trigger("ready").off("ready");
  113. }
  114. }
  115. });

看完代码,让我们再来看一下jQuery的思路:

  • Ⅰ. 调用rootjQuery的ready事件
  • Ⅱ. 调用jQuery.ready.promose(),若readyList为空,将readyList赋值为一个Deferred对象;否则将回调函数添加到readyList成功回调列表中,然后跳转到第7步
  • Ⅲ. 在jQuery.promise()中为DOM Ready事件添加监听器
  • Ⅳ. 将回调函数fn添加到readyList的成功回调列表中
  • Ⅴ. 当DOM Ready事件发生时,移除监听器,执行jQuery.ready()
  • Ⅵ. 在jQuery.ready()中将isReady置为true
  • Ⅶ. 执行readyList的成功回调列表中的方法

在jQuery.ready.promise()中,还涉及到了jQuery.Deferred和jQuery.Callback接口,这里暂不讨论,因为小Y也还没看,且听下回分析吧。

原文链接:http://www.html-js.com/article/Read-jQuery-jQuery-DOM-Ready

[转]jQuery DOM Ready的更多相关文章

  1. jquery dom ready, jqery2.1.1实现-源码分析

    本文链接http://www.cnblogs.com/Bond/p/4178311.html jquery document  ready的实现其很很简,虽说简单,其很很多人还是没去关注过它的实现.我 ...

  2. 【jQuery源码】DOM Ready

    一直以来,各种JS最佳实践都会告诉我们,将JS放在HTML的最后,即</body>之前,理由就是:JS会阻塞下载,而且,在JS中很有可能有对DOM的操作,放在HTML的最后,可以尽可能的保 ...

  3. jquery的ready方法(DOM是否加载完)详解与使用

    jquery的ready方法(准备DOM触发)还是比较复杂的,我们先看流程图:

  4. jQuery源码dom ready分析

    一.前言 在平时开发web项目时,我们使用jquery框架时,可能经常这样来使用$(document).ready(fn),$(function(){}),这样使用的原因是在浏览器把DOM树渲染好之前 ...

  5. jQuery之ready源码分析

    只要使用过jQuery的,想必对ready都不陌生,$(function(){})和$(document).ready(function(){})的使用更是习以为常. 要说到window.onload ...

  6. jQuery $(document).ready()和JavaScript onload事件

    jQuery $(document).ready()和JavaScript onload事件 Why we need a right time? 对元素的操作和事件的绑定需要等待一个合适的时机,可以看 ...

  7. jquery的ready事件的实现机制浅析

    页面初始化中,用的较多的就是$(document).ready(function(){//代码}); 或 $(window).load(function(){//代码}); 他们的区别就是,ready ...

  8. Web UI - Javascript之DOM Ready

    最近终于稍微适应了工作环境,终于可以让自己缓口气.于是决定要写点东西,算是督促.记录和提升自己的学习.代码的世界,你不轮它,以后就会被它轮.这个系列尽量保持在一周或两周更一篇,目标是在创造内容的时候更 ...

  9. jQuery $(document).ready()和window.onload

    jQuery $(document).ready()和window.onload 根据ready()方法的API说明http://api.jquery.com/ready/. 这个方法接收一个func ...

随机推荐

  1. iOS 力学动画生成器UIKit Dynamics 之碰撞效果讲解

    UIKit Dynamic是iOS7 新增的一组类和方法,可赋予UIView逼真的行为和特征,不需要写动画效果那些繁琐的代码,让开发人员能够轻松地改善应用的用户体验.一共有6个可用于定制UIDynam ...

  2. Redis集群维护、运营的相关命令与工具介绍

    Redis集群的搭建.维护.运营的相关命令与工具介绍 一.概述 此教程主要介绍redis集群的搭建(Linux),集群命令的使用,redis-trib.rb工具的使用,此工具是ruby语言写的,用于集 ...

  3. SQL 语句 使用附加和分离

    use mastergo declare @flg int --返回0表示成功 否则表示失败declare @msg varchar(50) --显示成功或失败的消息declare @dbname v ...

  4. LeetCode Best Time to Buy and Sell Stock II (简单题)

    题意: 股票买卖第2题.给出每天的股票价格,每次最多买一股,可以多次操作,但是每次在买之前必须保证身上无股票.问最大的利润? 思路: 每天的股票价格可以看成是一条曲线,能卖掉就卖掉,那么肯定是在上升的 ...

  5. C基础的练习集及测试答案(40-50)

    40.(课堂)打印杨辉三角型前10行 #if 0 40.(课堂)打印杨辉三角型前10行 思路分析: 一.打印十行杨辉三角得第十行长度为十,所以建立一个长度为十的数组,作为每行的数据存储 二.按 0-9 ...

  6. 如何在SAP云平台上使用MongoDB服务

    首先按照我这篇文章在SAP云平台上给您的账号分配MongboDB服务:如何在SAP云平台的Cloud Foundry环境下添加新的Service 然后从这个链接下载SAP提供的例子程序. 1. 使用命 ...

  7. java—三大框架详解,其发展过程及掌握的Java技术慨括

    Struts.Hibernate和Spring是我们Java开发中的常用关键,他们分别针对不同的应用场景给出最合适的解决方案.但你是否知道,这些知名框架最初是怎样产生的? 我们知道,传统的Java W ...

  8. 【BZOJ2427】[HAOI2010] 软件安装(缩点+树形DP)

    点此看题面 大致题意: 有\(N\)个软件,每个软件有至多一个依赖以及一个所占空间大小\(W_i\),只有当一个软件的直接依赖和所有的间接依赖都安装了,它才能正常工作并造成\(V_i\)的价值.求在容 ...

  9. Linux一键脚本合集vps

    首先,想说说一键脚本流行的原因何在? 众所周知的是,Linux 是占据大半壁江山的服务器系统,但在桌面上的占有率可就远不是那么回事儿了,使用和熟悉 Linux 的人远没有 Windows 多,但又因为 ...

  10. 2018.6.29 JavaScript

    一.使用JS数组实现冒泡排序 二.创建Teacher对象,添加(姓名.年龄.地址.学生对象[学生姓名,学生性别])属性 要求: 创建多个老师对象,每个老师下管理多个学生,显示每个老师下所有的学生信息 ...