今天为大家介绍的是jQuery源码dom ready分析的相关内容,希望大家会喜欢。
一、前言
在平时开发web项目时,我们使用jquery框架时,可能经常这样来使用(document).ready(fn),(function(){}),这样使用的原因是在浏览器把DOM树渲染好之前,javascript是无法操作没渲染好的DOM节点。
其实除了(document).ready(fn),(function(){})写法外,还有两种让dom渲染完之后执行js的写法:
$(document).on('ready', fn2)
//通过on事件绑定函数,通过trigger触发也可以达到j
Query.ready.promise().done(fn);
//通过这种方式也可以实现,jQuery.ready.promise()返回一个deferred对象,done(fn)添加回调方法
其中具体流程图如下(自己简单画了一下,有错请大家指正)
二、源码部分(建议看这部分是,先理解清楚deferred,promise)
①$(function(){}) =>到rootjQuery.ready(selector);
我们知道,jQuery是由new jQuery.fn.init(selector, context, rootjQuery)实例出来的,对接了两个参数,selector,context
// 构造函数,定义一个局部变量的jQueryjQuery = function (selector, context) {
// jQuery对象实际上是init的构造函数的引用return new jQuery.fn.init(selector, context, rootjQuery);
}
当我们使用(function()),则选择器selector参数就变成了funciton,jQuery.fn.init函数判断selector为Funtion时,又指向了
rootjQuery.ready(selector),就是(document).ready(fn);
rootjQuery = $(document)
jQuery.fn = jquery.prototype = {
init:function(selector,context,rootjQuery){
if (jQuery.isFunction(selector)) {
// 引用非静态成员ready方法,等价于$(document).ready(selector)return rootjQuery.ready(selector);}
②$(document).on("ready",fn) => 到jQuery(document).trigger("ready").off("ready");
要理解$(document).on("ready",fn),我们要看ready部分
//扩展方法到jquery***
jQuery.extend({
/**
记录监听DOMContentLoaded事件(DOM是否加载完成),加载完成设置为true(类似一个开关)
* type {Boolean} 默认为false,表示页面未加载完。当页面DOM加载完成,设置为true */isReady: false,
/**
需要预加载的观察者数量
* type {Number} 观察者数量 */readyWait: 1,
/**
DOM加载完成,执行预加载
* @param {Boolean} wait 为true表示锁定委托人,为false表示释放委托人*/ready: function (wait) {
if (wait === true ? --jQuery.readyWait : jQuery.isReady) {
return;
}
// 检测body是否存在(IE的一个bug),存在继续向下执行,不存在进入语句,执行setTimeout定时器,直到body存在if (!document.body) {
return setTimeout(jQuery.ready);
}
// 开关,记录DOM加载完成jQuery.isReady = true;
// 检测所有的观察者是否执行完成if (wait !== true && --jQuery.readyWait > 0) {
return;
}
// 委托人readyList通知观察者,开始执行回调函数,并将document作为这些函数的上下文环境
readyList.resolveWith(document, [jQuery]);
// 检测trigger方法是否存在,触发绑定在document上的事件,执行完成之后并解绑
// $(document).on('ready', fn2);
// $(document).ready(fn1);
//这里的fn1会先执行,自己的ready事件绑定的fn2回调后执行if (jQuery.fn.trigger) {
jQuery(document).trigger("ready").off("ready");
})
③$(document).ready(fn)
jQuery.fn = jquery.prototype = {
if (jQuery.isFunction(selector)) {
// 引用非静态成员ready方法return rootjQuery.ready(selector);
},
ready: function (fn) {
// Add the callback
// promise类似一种事件委托,相当于一个创建委托人(已创建则无需创建,通过readyList判断是否已创建),在DOM加载完成之后,委托人会通知观察者done去执行回调函数fn
//调委托函数,返回deferred对象,添加done(fn)回调函数
jQuery.ready.promise().done(fn);
return this;
}
}
最后dom ready相关部分源码详细如下:
// 构造函数,定义一个局部变量的jQueryjQuery = function (selector, context) {
// jQuery对象实际上是init的构造函数的引用return new jQuery.fn.init(selector, context, rootjQuery);
} // 就绪事件处理程序completed = function (event) {
// document.readyState 判断文档加载状态,'complete'代表文档已经完全加载 if (document.addEventListener || event.type === "load" || document.readyState === "complete") {
detach();
//清理方法
jQuery.ready();//执行延迟加载方法 }
} // 清理DOMContentLoaded事件处理程序,为DOM事件做好准备,触发jQuery.ready方法detach = function () {
// 标准的W3C监听事件if (document.addEventListener) {
//删除DOMContentLoaded监听事件document.removeEventListener("DOMContentLoaded", completed, false);
window.removeEventListener("load", completed, false);
} else {
// 针对IE,非标准的浏览器
// 删除onreadystatechange监听事件document.detachEvent("onreadystatechange", completed);
window.detachEvent("onload", completed);
}
}; var readyList,rootjQuery=$(document);
jQuery.fn = jquery.prototype = { if (jQuery.isFunction(selector)) {
// 引用非静态成员ready方法return rootjQuery.ready(selector);
},
ready: function (fn) {
// Add the callback
// promise类似一种事件委托,相当于一个创建委托人(已创建则无需创建,通过readyList判断是否已创建),在DOM加载完成之后,委托人会通知观察者done去执行回调函数fn
//调委托函数,返回deferred对象,添加done(fn)回调函数
jQuery.ready.promise().done(fn);
return this;
}}
//扩展方法到jquery****
jQuery.extend({
/**记录监听DOMContentLoaded事件(DOM是否加载完成),加载完成设置为true(类似一个开关)
* type {Boolean} 默认为false,表示页面未加载完。当页面DOM加载完成,设置为true */isReady: false,
/**
需要预加载的观察者数量
* type {Number} 观察者数量 */readyWait: 1,
/**
锁定或释放预加载委托人
* @param {Boolean} hold 为true表示锁定预加载委托人,为false表示释放委托人 */holdReady: function (hold) {
if (hold) {
jQuery.readyWait++;
} else {
jQuery.ready(true);
}},
/**
DOM加载完成,执行预加载
* @param {Boolean} wait 为true表示锁定委托人,为false表示释放委托人 */ ready: function (wait) {
// Abort if there are pending holds or we're already readyif (wait === true ? --jQuery.readyWait : jQuery.isReady) {
return;
}
// 检测body是否存在(IE的一个bug),存在继续向下执行,不存在进入语句,执行setTimeout定时器,直到body存在if (!document.body) {
return setTimeout(jQuery.ready);
} // 开关,记录DOM加载完成jQuery.isReady = true;
// 检测所有的观察者是否执行完成if (wait !== true && --jQuery.readyWait > 0) {
return;
}
// 委托人readyList通知观察者,开始执行回调函数,并将document作为这些函数的上下文环境
readyList.resolveWith(document, [jQuery]);
// 检测trigger方法是否存在,触发绑定在document上的事件,执行完成之后并解绑
// $(document).on('ready', fn2);
// $(document).ready(fn1);
//这里的fn1会先执行,自己的ready事件绑定的fn2回调后执行if (jQuery.fn.trigger) {
jQuery(document).trigger("ready").off("ready");
}})
// 预加载委托函数jQuery.ready.promise = function (obj) {
// 判断预加载委托人是否存在if (!readyList) {
// 创建一个预加载委托人readyList = jQuery.Deferred();
/**
W3C标准DOM浏览器
* 0-uninitialized:XML 对象被产生,但没有任何文件被加载。
* 1-loading:加载程序进行中,但文件尚未开始解析。
* 2-loaded:部分的文件已经加载且进行解析,但对象模型尚未生效。
* 3-interactive:仅对已加载的部分文件有效,在此情况下,对象模型是有效但只读的。
* 4-complete:文件已完全加载,代表加载成功。 */// 检测document内容是否加载完成,加载完成返回true,否者返回falseif (document.readyState === "