ready事件是当dom文档树加载完成后执行一个函数(不包含图片,css等),因此它的触发要早于load事件。用法:
- $(document).ready(fun) ;fun是一个函数,这样当dom树加载完毕后就会执行该匿名函数了
ready有一个简写,可以直接传入$(fun)即可,这是因为在jquey内部也定义了一个$(document)的jquery对象,和我们在上面的写法是一样的
ready事件和window的onload区别:
- ready事件 ;等dom树载完毕后就可以执行
- onload事件 ;等网页中所有的资源加载完毕后(包括图片,flash,音频,视频)才能执行
onload事件还可以绑定在某个图片上面,举个例子:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>document</title>
<script src="http://www.51sjk.com/Upload/Articles/1/0/267/267579_20210708020306926.js"></script>
</head>
<body>
<img src="http://www.51sjk.com/Upload/Articles/1/0/267/267579_20210708020306942.gif" alt="">
<script>
$(()=>console.log('dom树已加载完毕')) //ready事件
$('img').on('load',()=>console.log('图片已加载完毕')) //图片的加载事件
$(window).on('load',()=>console.log('资源已加载完毕')) //网页所有资源都加载完毕后的事件
</script>
</body>
</html>
这里我们用了箭头函数来写,代码很简单了,我们在绑定了一个ready事件,一个图片上的onload事件和window上的onload事件,加载后输出如下:

可以看到首先是ready事件的触发,然后是图片的onload事件,最后是window的onload事件的触发,此时所有资源都已经加载完了
源码分析
jquery的ready事件就是在document上绑定了一个domcontentloaded事件对象,对他进行了一下封装,domcontentloaded事件的原理可以看看看这篇文章,介绍得挺详细的:
jquery的ready事件是基于函数列表实现的,函数列表可以看这个连接:
当我们调用$(fun)去执行一个ready事件的时候首先会执行入口模块里的逻辑,与ready相关的如下:
init: function( selector, context, rootjquery ) {
var match, elem, ret, doc;
// handle $(""), $(null), or $(undefined)
if ( !selector ) {
return this;
}
// handle $(domelement)
if ( selector.nodetype ) {
this.context = this[0] = selector;
this.length = 1;
return this;
}
// the body element only exists once, optimize finding it
if ( selector === "body" && !context && document.body ) {
this.context = document;
this[0] = document.body;
this.selector = selector;
this.length = 1;
return this;
}
// handle html strings
if ( typeof selector === "string" ) {
/*略*/
} else if ( jquery.isfunction( selector ) ) { //如果参数selector是函数,则认为是绑定ready事件
return rootjquery.ready( selector ); //则执行rootjquery.ready()方法,并把selector作为参数传入
}
/*略*/
},
rootjquery是jquery内部定义的一个局部变量,是一个jquery实例,如下:
rootjquery = jquery(document); //第917行,保存了document对象引用的jquery实例
在入口模块引用rootjquery.ready()也就是执行了rootjquery实例对象上的ready方法(该方法是定义在原型上的),如下:
jquery.fn = jquery.prototype = {
ready: function( fn ) {
// attach the listeners
jquery.bindready(); //先执行jquery.bindready()绑定ready事件(实际上绑定的是domcontentloaded或onreadystatechange事件)
// add the callback
readylist.add( fn ); //为函数列表readylist增加一个函数
return this;
}
}
jquery.bindready()是一个静态方法,用于绑定事件的,内部会初始化readylist为一个jquery.callbacks( "once memory" )函数列表对象
然后执行readylist.add( fn )将fn函数保存到函数列表readylist里面。
jquery.bindready()的实现如下:
jquery.extend({
bindready: function() { //初始化ready事件监听函数列表readylist,并为document对象绑定ready事件主监听函数domcontentloaded
if ( readylist ) {
return;
}
readylist = jquery.callbacks( "once memory" ); //调用jquery.callbacks(flags)ready事件监听函数列表readylist,同时传入once和memory标记。
// catch cases where $(document).ready() is called after the
// browser event has already occurred.
if ( document.readystate === "complete" ) { //如果文档已经就绪,则调用jquery.ready(wait)执行ready事件监听函数列表readylist
// handle it asynchronously to allow scripts the opportunity to delay ready
return settimeout( jquery.ready, 1 ); //通过settimeout()异步执行方法jquery.ready(wait),以允许其他脚本延迟ready事件的触发。
}
// mozilla, opera and webkit nightlies currently support this event
if ( document.addeventlistener ) { //在ie9+及以上浏览器绑定domcontentloaded事件
// use the handy event callback
document.addeventlistener( "domcontentloaded", domcontentloaded, false ); //把监听函数domcontentloaded绑定到document对象的domcontentloaded事件上
// a fallback to window.onload, that will always work
window.addeventlistener( "load", jquery.ready, false );
// if ie event model is used
} else if ( document.attachevent ) {
// 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 toplevel = false;
try {
toplevel = window.frameelement == null;
} catch(e) {}
if ( document.documentelement.doscroll && toplevel ) {
doscrollcheck();
}
}
},
/*略*/
})
这里我们调用document.addeventlistener在document上绑定了一个domcontentloaded事件,这样当dom树加载完后就会执行domcontentloaded函数了,domcontentloaded函数的定义如下:
if ( document.addeventlistener ) { //如果是ie9+和其他浏览器
domcontentloaded = function() {
document.removeeventlistener( "domcontentloaded", domcontentloaded, false ); //先移除document的domcontentloaded事件
jquery.ready(); //再调用jquery.ready()执行ready事件监听函数
};
} else if ( document.attachevent ) {
domcontentloaded = function() {
// make sure body exists, at least, in case ie gets a little overzealous (ticket #5443).
if ( document.readystate === "complete" ) {
document.detachevent( "onreadystatechange", domcontentloaded );
jquery.ready();
}
};
}
函数内首先会移除domcontentloaded事件,然后调用jquery.ready()事件,这是dom树触发后的事件了(我们在jquery.fn.ready()内执行了readylist.add( fn )增加的函数都会依次触发),如下:
jquery.extend({
isready: false,
ready: function( wait ) { //实际执行的函数 触发ready事件监听函数列表readylist和数据缓存对象中的ready事件监听函数。
// either a released hold or an domready/load event and not yet ready
if ( (wait === true && !--jquery.readywait) || (wait !== true && !jquery.isready) ) { //如果wait是true且jquery.readywait等于0 或者 wait不是true且jquery.isready是false 则执行 初始化jquery.isready为false的
// make sure body exists, at least, in case ie gets a little overzealous (ticket #5443).
if ( !document.body ) {
return settimeout( jquery.ready, 1 );
}
// remember that the dom is ready
jquery.isready = true; //设置jquery.inready为true,表示ready事件已就绪。
// if a normal dom ready event fired, decrement, and wait if need be
if ( wait !== true && --jquery.readywait > 0 ) {
return;
}
// if there are functions bound, to execute
readylist.firewith( document, [ jquery ] ); //执行ready事件监听函数readylist,上下文是document(即关键词this),[jquery]是ready事件监听函数的参数。
// trigger any bound ready events
if ( jquery.fn.trigger ) {
jquery( document ).trigger( "ready" ).off( "ready" );
}
}
},
/*略*/
})
writer by:大沙漠 qq:22969969
最后调用readylist.firewith()方法去触发回掉函数列表里的每个函数。
約啶啈冨19506237