• 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()方法去触发回掉函数列表里的每个函数。

  • 相关阅读:
    CDOJ 92 – Journey 【LCA】
    LCA-Tarjan算法
    【模板】无向图的割顶
    Codeforces 190E
    TwoSAT算法模板
    【转】STL之二分查找 (Binary search in STL)
    【转】数论模板
    【转】计算几何模板
    【转】string常用函数
    Codeforces 245G Suggested Friends
  • 原文地址:https://www.cnblogs.com/greatdesert/p/11720567.html
Copyright © 2020-2023  润新知