jQuery版本:2.0.3

DOM加载有关的扩展

  • isReady:DOM是否加载完(内部使用)
  • readyWait:等待多少文件的计数器(内部使用)
  • holdReady():推迟DOM触发
  • ready():准备DOM触发。
  • jQuery.ready.promise=function(){};  监听DOM的异步操作(内部使用)

一、$(function(){})和原生window.onload的关系

这个在面试中也是经常会被问到的。从下面几个角度来分析一下它们的区别

1、执行时机

页面加载,先加载节点,再加载文件,比如img文件,flash等。

$(function(){})DOM加载完执行。可能DOM元素关联的东西并没有加载完。

window.onload等节点和文件都加载完执行。

对应的事件监听

jQuery用的是DOMContentLoaded事件。

DOMDContentLoaded:原生DOM加载事件,这个事件触发代表DOM加载完了。

我之前写过一篇文章的页面加载时间分析里也有提到。

onload对应的是load事件。

2、个数

window.onload不能写多个,后面的会覆盖前面的。

$(function(){})可以写多个。都会执行 。

3、简化写法

$(function(){})是$(document) .ready(function(){});的简化写法。

window.onload没有简化写法。

二、jQurey如何实现DOM ready的

jQuery直接调用DOMContentLoaded来实现DOM的ready。但是DOMContentLoaded和onLoad一样,浏览器只执行一次,jQuery用什么判断是否已经执行过呢?document.readyState就是判断这个的依据。

readyState是document的属性,总共有3个值:

  • loading:文档正在加载中
  • interactive:文档已经加载完成,正在进行css和图片等资源的加载
  • complete:文档的所以资源加载完成

判断完之后如何回调呢?就是用Promise。jQuery通过new一个$.Deferred(promise)对象来实现对DOM的ready的回调,在DOMContentLoaded中将这个promise给resolve掉,这样就执行了之前注册的回调函数,同时后面新注册的回调也会立刻执行。

但是在调用promise之前,jQuery执行了一次setTimeout,因为jQuery.Promise是不会产生异步的,这和标准的promise规范是不一样的,所有jQuery自己又手动做了一次setTimeout来实现异步。这样使得无论使用在DOM的ready之前注册的回调还是之后注册的回调都会在异步中执行。

三、源码整体实现逻辑

$(fn)==>new一个 jQuery.fn.init(fn)==>返回$(document).ready( fn)。也就是说

  • $(function(){}) =》
  • 调用$(document).ready(function(){})=》
  • 相当于$().ready(fn)实例方法=》
  • 调用的jQuery.ready.promise().done(fn)=》
  • jQuery.ready.promise中不管if还是else最终都是调用jQuery.ready(),并返回promise=》
  • jQuery.ready()里面重点是readyList.resolveWith( document, [ jQuery ] );已完成。至此DOM加载完毕就可以调用fn了。

四、实现细节

1、重点是:jQuery.ready.promise()方法【巧妙

如果DOM已经加载完成了,调用jQuery.ready()这个工具方法;

如果DOM没加载完,监听DOMContentLoaded事件和load事件,等事件发生时回调completed(),最终也是调用jQuery.ready()这个工具方法;

var    // A central reference to the root jQuery(document)
rootjQuery, // The deferred used on DOM ready
readyList; jQuery.ready.promise = function( obj ) {
if ( !readyList ) { //第一次readyList为空可以进来,后续就进不来if了,只执行一次 readyList = jQuery.Deferred(); //第一步,创建延迟对象
if ( document.readyState === "complete" ) { //DOM加载完成的标志就是document.readyState为complete,如果DOM已经加载好了就直接调工具方法jQuery.ready。
setTimeout( jQuery.ready );//加定时器是为了兼容IE
} else {//DOM没有加载完检测,即检测了DOMContentLoaded事件,也检测了load事件;最终走回调completed函数
// Use the handy event callback
document.addEventListener( "DOMContentLoaded", completed, false );
// A fallback to window.onload, that will always work
window.addEventListener( "load", completed, false ); //因为火狐浏览器会缓存load事件,为了第一时间相应所以对load也监听了
}
}
return readyList.promise( obj );
};

completed回调函数如下,最终调用的也是jQuery.ready()。

    // The ready event handler and self cleanup method
completed = function() {
//不管是DOMContentLoaded事件还是load发生,都会取消2个事件监听
//jQuery.ready()只会触发一次
document.removeEventListener( "DOMContentLoaded", completed, false );
window.removeEventListener( "load", completed, false );
jQuery.ready();
};

2、jQuery.ready()工具方法做了些什么

先做个测试:

   $(function(arg){
alert(this); //[object HTMLDocument]
alert(arg); //jQuery函数
})

再做个测试:

除了$(function(){});$(document).ready(function(){}),也可以把ready当做事件来处理如下。

<script>
$(document).on("ready",function(){
alert(123); //
});
</script>

之所以会自动弹出123,是因为在jQuery源码中用这句话jQuery( document ).trigger("ready").off("ready");来主动触发了ready事件,触发后再取消掉。

// Handle when the DOM is ready
ready: function( wait ) { //参数和holdReady有关 // Abort if there are pending holds or we're already ready
if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
return;
} // Remember that the DOM is ready
jQuery.isReady = true; // If a normal DOM Ready event fired, decrement, and wait if need be
if ( wait !== true && --jQuery.readyWait > 0 ) {
return;
}
//第一步看这里重点,resolveWith改变状态的时候传参了,给done中方法fn传入了参数,
//document是fn的this指向,jQuery是参数
// If there are functions bound, to execute
readyList.resolveWith( document, [ jQuery ] );
//跟主动触发有关
// Trigger any bound ready events
if ( jQuery.fn.trigger ) {
jQuery( document ).trigger("ready").off("ready");
}
},

跟ready的参数有关的有一个holdReady()。

先做个测试

$.holdReady(true);
$(function () {
alert(123); //调用了holdReady并传参true,就不能弹出123了
});

可以推迟,也可以释放推迟,释放了以后就可以触发了。

$.holdReady(true);
$.holdReady(false);
$(function () {
alert(123); //释放了holdReady,就弹出123
});

这个有什么用?

比如:

$.getScript('js/a.js',function(){ //异步加载,不会影响后续代码执行。可能会产生一个问题,alert(2)先执行了a.js还没有加载完

})

$(function () {
alert(2);//先弹2,后弹出1
});

很多时候引入外部文件的时候,都想等外部文件或者插件加载完,再去触发操作,操作可能用到a.js。现在这个顺序不对,会出问题。

怎样解决这个问题?用holdReady()方法。

    $.holdReady(true); //在这里先hold住
$.getScript('js/a.js', function () { //异步加载,不会影响后续代码执行。可能会产生一个问题,alert(2)先执行了a.js还没有加载完
$.holdReady(false); //加载完释放,不hold了就可以弹2了
}) $(function () {
alert(2);//先弹2,后弹出1
});

再深入一点,holdReady() 要针对的文件可能不止一个,有很多个,所以要等所有的文件都加载完再执行,所以源码中有一个计数的readyWait。

源码中定义了一个等待栈变量——readyWait,每次执行$.holdReady(true)都会增加压栈,而每次$.holdReady()执行都会弹栈,等空栈的时候就执行jQuery.ready函数,即将promise给resolve掉。

// Is the DOM ready to be used? Set to true once it occurs.
isReady: false,
// A counter to track how many items to wait for before
// the ready event fires. See #6781
readyWait: 1, //第二步看这里
//holdReady推迟DOM的触发
// Hold (or release) the ready event
holdReady: function( hold ) {
if ( hold ) {
jQuery.readyWait++;//hold为真,让readyWait加加处理
} else {
jQuery.ready( true );
}
}, // Handle when the DOM is ready
ready: function( wait ) { //参数和holdReady有关 // Abort if there are pending holds or we're already ready
//readyWait为0时就不用hold了,执行后面的操作
if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
return;
} // Remember that the DOM is ready
jQuery.isReady = true; //默认是false // If a normal DOM Ready event fired, decrement, and wait if need be
if ( wait !== true && --jQuery.readyWait > 0 ) {
return;
}
//第一步看这里重点,resolveWith改变状态的时候传参了,给done中方法fn传入了参数,
//document是fn的this指向,jQuery是参数
// If there are functions bound, to execute
readyList.resolveWith( document, [ jQuery ] );
//跟主动触发有关
// Trigger any bound ready events
if ( jQuery.fn.trigger ) {
jQuery( document ).trigger("ready").off("ready");
}
},

刚开始看源码,很多地方理解也不到位,解释可能也不清楚。

参考:

http://www.cnblogs.com/aeexiaoqiang/p/6525702.html

本文作者starof,因知识本身在变化,作者也在不断学习成长,文章内容也不定时更新,为避免误导读者,方便追根溯源,请诸位转载注明出处:http://www.cnblogs.com/starof/p/6856572.html有问题欢迎与我讨论,共同进步。

jquery源码 DOM加载的更多相关文章

  1. jQuery源码dom ready分析

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

  2. jQuery源码-dom操作之jQuery.fn.html

    写在前面 前面陆陆续续写了jQuery源码的一些分析,尽可能地想要cover里面的源码细节,结果导致进度有些缓慢.jQuery的源码本来就比较晦涩,里面还有很多为了解决兼容问题很引入的神代码,如果不g ...

  3. 深入理解 spring 容器,源码分析加载过程

    Spring框架提供了构建Web应用程序的全功能MVC模块,叫Spring MVC,通过Spring Core+Spring MVC即可搭建一套稳定的Java Web项目.本文通过Spring MVC ...

  4. jQuery源码-dom操作之jQuery.fn.text

    写在前面 jQuery.fn.text在jQuery是个使用频率比较高的接口,它的作用无非是设置/获取dom节点的内容文本,下文会通过几个简单的例子来说明.text()接口的使用,以及最后会对源码进行 ...

  5. 2.2 spring5源码 -- ioc加载的整体流程

    之前我们知道了spring ioc的加载过程, 具体如下图. 下面我们就来对照下图, 看看ioc加载的源代码. 下面在用装修类比, 看看个个组件都是怎么工作的. 接下来是源码分析的整体结构图. 对照上 ...

  6. Spring 源码学习——加载 Bean

    继上次注册 bean 之后好久没更新,这两天有空查了查资料也自己看了看 spring BeanFactory 的 getBean(beanName); 这个方法.因时间有限不能像之前那样复制代码并一行 ...

  7. jQuery学习(监听DOM加载)

    jQuery的extend方法 function njQuery() { } /* njQuery.extend = function (obj) { // 此时此刻的this就是njQuery这个类 ...

  8. jQuery实现DOM加载方法源码分析

    传统的判断dom加载的方法 使用 dom0级 onload事件来进行触发所有浏览器都支持在最初是很流行的写法 我们都熟悉这种写法: window.onload=function(){ ... }  但 ...

  9. 从jQuery源码阅读看 dom load

    最近两天不忙的时候再回过来研究一下jquery的源码,看到$(document).ready()时,深入的研究了一下dom的加载问题. 我们都知道,window.onload可以解决我们的js执行时机 ...

随机推荐

  1. 数据库DML操作(DCL了解)

    DQL:SELECT * FROM 表名DML(数据操作语言,它是对表记录的操作(增.删.改)!)1. 插入数据* INTERT INTO 表名(列名1,列名2, ...) VALUES(列值1, 列 ...

  2. 【C++】指针与引用的区别

    本文主要总结在C++中指针与引用的区别. 从定义与性质来看指针与引用有如下区别: 指针表示的是一块变量的地址 引用表示一个变量的别名. 因此指针变量需要占用空间(一个指针变量在32位系统下占用4字节, ...

  3. js的eval函数

    今天在看代码的时候发现了一个eval函数,不解其意,通过查询之后,终于知道了,下面是我的总结. eval函数可以将字符串生成语句执行,格式为:eval(coding),类似于sql语句里面的exec( ...

  4. VMware workstation转到vsphere解决办法

    一.前因 上一篇http://www.cnblogs.com/cuncunjun/p/6611837.html 中提到,我想把本地的vmware workstation的虚拟机拷贝到服务器上,因为鄙人 ...

  5. Hybris license过期的解决办法

    license过期编译是可以通过的,但是启动服务会报错: LICENSE VERIFICATION HAS FAILED! Your demo/develop license has expired, ...

  6. GPU加速有坑?

    大多数人都知道有动画的地方可以使用GPU来加速页面渲染. 例如,做优化的时候,将使用left和top属性的动画修改成使用transform属性的CSS动画.或者听到别人教你使用transform:tr ...

  7. jmeter 使用jmeter 录制web脚本

    1.打开jmeter.鼠标右击工作台.添加HTTP代理服务器 2.设置端口号.目标控制器.分组 3.添加查看结果树 4.点击启动.确定完成 5.打开浏览器直接进行操作.就可以看到所录制的脚本信息

  8. ios runtime部分事例方法说明

    一.场景--动态改变变量 unsigned ; Ivar *ivar = class_copyIvarList([self.person class], &count); ; i<cou ...

  9. angular的$http.post()提交数据到Java后台接收不到参数值问题的解决方法

    本文地址:http://www.cnblogs.com/jying/p/6733408.html   转载请注明出处: 写此文的背景:在工作学习使用angular的$http.post()提交数据时, ...

  10. LINQ to XML编程之编程基础

    1.声明,LINQ to XML让xml的创建变得非常简单. XDocument myDocument = new XDocument( new XDeclaration("1.0" ...