DOM加载有关的扩展
isReady
: DOM是否加载完(内部使用)readyWait
: 等待多少文件的计数器(内部使用)holdReady()
: 推迟DOM触发ready()
: 准备DOM触发。jQuery.ready.promise=function(){};
监听DOM的异步操作(内部使用)
$(function(){})和原生window.onload的关系
执行时机
- 页面加载,先加载节点,再加载文件,比如img文件,flash等。
$(function(){})DOM
加载完执行。可能DOM元素关联的东西并没有加载完。window.onload
等节点和文件都加载完执行。- 对应的事件监听
- jQuery用的是
DOMContentLoaded
事件 — 原生DOM加载事件,这个事件触发代表DOM加载完了。 - onload对应的是load事件
个数
window.onload
不能写多个,后面的会覆盖前面的。$(function(){})
可以写多个。都会执行 。
简化写法
$(function(){})
是$(document) .ready(function(){});
的简化写法。window.onload
没有简化写法。
jQuery如何实现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
[readyList.resolveWith( document, [ jQuery ] );
]掉,这样就执行了之前注册的回调函数[jQuery.ready.promise().done( function(){} );
],同时后面新注册的回调也会立刻执行[因为返回的是同一个promise,每次都会执行jQuery.ready.promise().done( function(){} );
,同时readyList.resolveWith( document, [ jQuery ] );
在DOM加载完毕之后已经执行过了,之后所有的done的context都是document,args都是jQuery]。
但是在调用promise之前,jQuery执行了一次setTimeout,因为jQuery.Promise是不会产生异步的,这和标准的promise规范是不一样的,所有jQuery自己又手动做了一次setTimeout来实现异步。这样使得无论使用在DOM的ready之前注册的回调还是之后注册的回调都会在异步中执行。
$(function(){}); 的加载过程
当页面DOM树加载完毕之后触发。注意的是它是dom树加载完毕,并不是页面所有资源加载完毕,例如图片,音视频等还没加载前触发。
window.onload
是js原生的页面所有资源加载完毕才会触发。
$(function(){});
相当于
$(document).ready(function(){});
相当于
jQuery.ready.promise().done( function(){} );
ready: function( fn ) {
jQuery.ready.promise().done( fn );
return this;
}
所以下面来解释jQuery.ready.promise()的含义
// 在开头有定义
// The deferred used on DOM ready
var readyList
// 监听DOM的异步操作(内部使用)
jQuery.ready.promise = function( obj ) {
//第一次readyList为空可以进来,后续就进不来if了,只执行一次
if ( !readyList ) {
// 先创建一个延迟对象
// 延迟对象的状态有完成,未完成等其它的状态,这些状态在外部是可以被修改的,但promise是不可以被修改的,所以返回promise
readyList = jQuery.Deferred();
// DOM已经加载好的标志就是document.readyState === "complete"
if ( document.readyState === "complete" ) {
// 加定时器是为了兼容IE
setTimeout( jQuery.ready );
} else {
// 当纯HTML被完全加载以及解析时,DOMContentLoaded 事件会被触发,而不必等待样式表,图片或者子框架完成加载。
// window.onload() 方法用于在网页加载完毕后立刻执行的操作,即当 HTML 文档加载完毕后,立刻执行某个方法
// 回调函数,调用completed
// 为什么既要监测DOM,又要检测load,DOM加载要高于loader,有些浏览器会缓存load这个事件,如果缓存了这个事件,就有可能会先触发这个事件
// 因为火狐浏览器会缓存load事件,为了第一时间相应所以对load也监听了
document.addEventListener( "DOMContentLoaded", completed, false );
window.addEventListener( "load", completed, false );
}
}
// deferred.promise() 函数返回 Deferred(延迟)的 Promise 对象。
return readyList.promise( obj );
};
监听器后面又调用了completed
// 在最开始定义过completed函数
// The ready event handler and self cleanup method
completed = function() {
// 移除监听器
document.removeEventListener( "DOMContentLoaded", completed, false );
window.removeEventListener( "load", completed, false );
jQuery.ready();
};
当DOM加载完毕之后都调用了jQuery.ready()
// 准备DOM触发
// jQuery.ready()
// 参数和holdReady有关
ready: function( wait ) {
// 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是参数
// resolveWith(context [, args]):类似于resolve(args)方法的功能,只是激发done()方法添加的函数时将传入context和args参数。
readyList.resolveWith( document, [ jQuery ] );
// Trigger any bound ready events
// 跟主动触发有关
if ( jQuery.fn.trigger ) {
jQuery( document ).trigger("ready").off("ready");
}
}
测试this和arg
$(function(arg){
alert(this); //[object HTMLDocument]
alert(arg); //jQuery函数
})
推迟DOM触发
- 示例:
$.holdReady(true); $(function () { alert(123); //调用了holdReady并传参true,就不能弹出123了}); // 可以推迟,也可以释放推迟,释放了以后就可以触发了。 $.holdReady(true); $.holdReady(false); $(function () { alert(123); //释放了holdReady,就弹出123}); // 引入外部文件的时候,都想等外部文件或者插件加载完,再去触发操作 $.holdReady(true); //在这里先hold住 $.getScript('js/a.js', function () { // 先加载完,后弹出2 $.holdReady(false); }) $(function () { alert(2); }); // holdReady() 要针对的文件可能不止一个,有很多个,所以要等所有的文件都加载完再执行,所以源码中有一个计数的readyWait // 源码中定义了一个等待栈变量——readyWait,每次执行$.holdReady(true)都会增加压栈,而每次$.holdReady()执行都会弹栈,等空栈的时候就执行jQuery.ready函数,即将promise给resolve掉
- 代码:
holdReady: function( hold ) { if ( hold ) { // 储存需要等待完成的事件个数 jQuery.readyWait++; } else { jQuery.ready( true ); } } ready: function( wait ) { // 如果要执行ready函数,首先要检查--jQuery.readyWait // 默认为false,DOM加载完一次之后,isReady为true /** * $.holdReady(true); //在这里先hold住 (readyWait = 2) $.getScript('js/a.js', function () { // 先加载完,后弹出2 $.holdReady(false); (readyWait = 1) }) $(function () { alert(2); (readyWait = 0) }); */ 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是参数 readyList.resolveWith( document, [ jQuery ] ); // Trigger any bound ready events // 跟主动触发有关 if ( jQuery.fn.trigger ) { jQuery( document ).trigger("ready").off("ready"); } }
主动触发ready事件
<script>$(document).on("ready",function(){
alert(123); //123});</script>
// 跟主动触发有关
if ( jQuery.fn.trigger ) {
jQuery( document ).trigger("ready").off("ready");
}
- trigger方法
// trigger() 方法触发被选元素上指定的事件以及事件的默认行为(比如表单提交)。 $( "#old" ).click(function() { $( "input" ).trigger( "focus" ); });
- off方法
// off() 方法通常用于移除通过 on() 方法添加的事件处理程序。 $("button").click(function(){ $("p").off("click"); });