jQuery 源码分析(十八) ready事件详解

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://libs.baidu.com/jquery/1.11.1/jquery.min.js"></script>
</head>
<body>
    <img src="https://www.cnblogs.com/images/logo_small.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事件的原理可以看看看这篇文章,介绍得挺详细的:https://www.cnblogs.com/caizhenbo/p/6679478.html

jQuery的ready事件是基于函数列表实现的,函数列表可以看这个连接:https://www.cnblogs.com/greatdesert/p/11433365.html

当我们调用$(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()方法去触发回掉函数列表里的每个函数。

原文地址:https://www.cnblogs.com/greatdesert/p/11720567.html

时间: 2024-11-03 21:00:52

jQuery 源码分析(十八) ready事件详解的相关文章

jQuery 源码分析(十一) 队列模块 Queue详解

队列是常用的数据结构之一,只允许在表的前端(队头)进行删除操作(出队),在表的后端(队尾)进行插入操作(入队).特点是先进先出,最先插入的元素最先被删除. 在jQuery内部,队列模块为动画模块提供基础功能,负责存储动画函数.自动出队并执行动画函数,同时还要确保动画函数的顺序执行. jQuery的静态方法含有如下API: $.queue(elem,type,data) ;返回或修改匹配元素关联的队列,返回最新的队列,参数如下:   elem ;DOM元素或JavaScript对象 type  ;

jQuery 源码分析(十四) 数据操作模块 类样式操作 详解

jQuery的属性操作模块总共有4个部分,本篇说一下第3个部分:类样式操作部分,用于修改DOM元素的class特性的,对于类样式操作来说,jQuery并没有定义静态方法,而只定义了实例方法,如下: addClass(value) ;为匹配元素集合中的每个元素添加一个或多个类样式,通过修改DOM属性className来修改类样式,value可以是个以空格分隔的类样式或者一个函数(返回一个或多个以空格分隔的类样式) hasClass(selector)   ;检测匹配元素中的任意元素是否含有指定的类

jQuery 源码分析(十五) 数据操作模块 val详解

jQuery的属性操作模块总共有4个部分,本篇说一下最后一个部分:val值的操作,也是属性操作里最简单的吧,只有一个API,如下: val(vlaue)        ;获取匹配元素集合中第一个元素的当前值,或者设置匹配元素集合中每个元素的值,有三种用法: val()        ;没有参数,获取第一个匹配元素的当前值 val(value)      ;为每个匹配元素设置value值        ;如果为null则表示设置值为空 val(func)       ;设置每个匹配元素为函数fun

jQuery 源码分析(十九) DOM遍历模块详解

jQuery的DOM遍历模块对DOM模型的原生属性parentNode.childNodes.firstChild.lastChild.previousSibling.nextSibling进行了封装和扩展,用于在DOM树中遍历父元素.子元素和兄弟元素. 可以通过jQuery的实例来访问,方法如下: parent()             ;获取匹配元素的父元素 parents(selector)         ;获取匹配元素的所有祖先元素                        ;s

ZRender源码分析5:Shape绘图详解

回顾 上一篇说到:ZRender源码分析4:Painter(View层)-中,这次,来补充一下具体的shape 关于热区的边框 以圆形为例: document.addEventListener('DOMContentLoaded', function () { var canvasDom = document.getElementById('canvasId'), context = canvasDom.getContext('2d'); context.lineWidth = 50; cont

Android源码分析--Handler和Looper机制详解

在Android系统中的应用程序,与Java的应用程序相同,都是靠消息驱动,简单的说就是:有一个消息队列,我们可以不断的向这个消息队列中添加消息,并从中取出消息,处理消息.Android中与此工作相关的主要是由Handler,Looper以及Message来完成. Looper类:为一个线程运行着一个消息循环,内部有一个消息队列,每一个线程只允许最多存在一个Looper: Handler类:允许你向一个线程的消息队列中发送消息,处理消息: Message类:消息类. 使用样例 首先,我们通过一个

Vue-Router 源码分析(七) VueRouter.push()的详解

通过VueRouter实例的push()操作,可以进行路由跳转,对于<router-link/>组件来说,它绑定的是click事件,最后也是通过执行push()方法来进行路由跳转的. 对于push()方法来说,一共可以传入三种形式的参数: 字符串形式,值为路劲 含有name的对象形式,可以搭配params属性传递参数 含有path的对象形式 举个栗子: <div id="app"> <button @click="test">测试

JQuery源码分析(八)

jQuery的each迭代器 jQuery的each方法从使用上就要分2种情况: $.each()函数 $(selector).each() $.each()函数和$(selector).each()是不一样的,后者是专门用来遍历一个jQuery对象的,是为jQuery内部服务的. $.each()函数可用于迭代任何集合,无论是"名/值"对象(JavaScript对象)或数组.在迭代数组的情况下,回调函数每次传递一个数组索引和相应的数组值作为参数. (该值也可以通过访问this关键字得

ZRender源码分析6:Shape对象详解之路径

开始 说到这里,就不得不提SVG的路径操作了,因为ZRender完全的模拟了SVG原生的path元素的用法,很是强大. 关于SVG的Path,请看这里: Path (英文版) 或者 [MDN]SVG教程(5) 路径 [译] (中文版), 很明显的是canvas中的路径没有SVG的用着舒服,那到底ZRender是如何实现的呢,让我给你娓娓道来(不过要想继续进行下去,上面的SVG的PATH必须了解.). 示例 打开API,shape.path,可以看到,path的配置有MLHVCSQTZ等字母组成的