Detailed explanation of jQuery source code analysis ready event

Keywords: Javascript JQuery IE

The ready event is to execute a function (excluding pictures, css, etc.) after the DOM document tree is loaded, so it is triggered earlier than the load event. Usage:

  • $(document).ready(fun); fun is a function that executes the anonymous function when the DOM tree is loaded

There is a shorthand for ready, which can be directly passed in $(fun). This is because there is also a $(document) jQuery object defined in jQuery, which is the same as our writing above

The difference between ready event and window onload is as follows:

  • ready event; it can be executed after the dom tree is loaded
  • onload event; can only be executed after all resources in the web page are loaded (including pictures, flash, audio and video)

The onload event can also be bound to a picture. For example:

<!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 Tree loaded'))                        //ready Event
        $('img').on('load',()=>console.log('Image loaded'))        //Loading events for pictures
        $(window).on('load',()=>console.log('Resource loaded'))       //Event after all resources of the web page have been loaded        
    </script>
</body>
</html>

Here we use the arrow function to write. The code is very simple. We bind a ready event, an onload event on the picture and an onload event on the window. After loading, the output is as follows:

You can see that the first is the triggering of the ready event, then the onload event of the picture, and finally the window onload event. At this time, all resources have been loaded

 

Source code analysis

jquery's ready event is to bind a DOMContentLoaded event object to the document and encapsulate it. See this article for the principle of DOMContentLoaded event, which is introduced in detail: https://www.cnblogs.com/caizhenbo/p/6679478.html

The ready event of jQuery is implemented based on the function list. The function list can be seen in this connection: https://www.cnblogs.com/greatdesert/p/11433365.html

When we call $(fun) to execute a ready event, we will first execute the logic in the entry module, as follows:

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" ) {
        /*slightly*/
    } else if ( jQuery.isFunction( selector ) ) {            //If parameters selector If it's a function, it's a binding ready Event
        return rootjQuery.ready( selector );                    //Then execute rootjQuery.ready()Method, and selector Passed in as a parameter
    }

    /*slightly*/
},

rootjQuery is a local variable defined within jQuery and an instance of jQuery, as follows:

rootjQuery = jQuery(document);                       //Line 917, saved document Object referenced jQuery Example

In the entry module, rootjQuery.ready() is used to execute the ready method on the rootjQuery instance object (this method is defined on the prototype), as follows:

jQuery.fn = jQuery.prototype = {
    ready: function( fn ) {
        // Attach the listeners
        jQuery.bindReady();                 //First execution jQuery.bindReady()binding ready Event(In fact, the binding is DOMContentLoaded or onreadystatechange Event)

        // Add the callback
        readyList.add( fn );                //List of functions readyList Add a function

        return this;
    }
}

jQuery.bindReady() is a static method used to bind events. The readyList will be initialized internally as a jQuery. Callbacks ("once memory") function list object

Then execute readyList. Add (fn) to save the fn function in the function list readyList.

The implementation of jQuery.bindReady() is as follows:

jQuery.extend({
    bindReady: function() {                            //Initialization ready Event listening function list readyList,Also for document Object binding ready Event main listening function DOMContentLoaded
        if ( readyList ) {
            return;
        }

        readyList = jQuery.Callbacks( "once memory" );                        //call jQuery.Callbacks(flags)ready Event listening function list readylist,Simultaneous introduction once and memory Mark.

        // Catch cases where $(document).ready() is called after the
        // browser event has already occurred.
        if ( document.readyState === "complete" ) {                            //If the document is ready, call jQuery.ready(wait)implement ready Event listening function list readyList
            // Handle it asynchronously to allow scripts the opportunity to delay ready
            return setTimeout( jQuery.ready, 1 );                                //adopt setTimeout()Asynchronous execution method jQuery.ready(wait),To allow other scripts to delay ready Event.
        }

        // Mozilla, Opera and webkit nightlies currently support this event
        if ( document.addEventListener ) {                                     //stay IE9+And above browser binding DOMContentLoaded Event
            // Use the handy event callback
            document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );         //Monitor function DOMContentLoaded Bound to document Object DOMContentLoaded Incident

            // 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();
            }
        }
    },
    /*slightly*/
})

Here we call document.addEventListener to bind a DOMContentLoaded event on the document, so that when the DOM tree is loaded, the DOMContentLoaded function will be executed. The definition of DOMContentLoaded function is as follows:

if ( document.addEventListener ) {         //If it is IE9+And other browsers
    DOMContentLoaded = function() {
        document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );    //Remove first document Of DOMContentLoaded Event
        jQuery.ready();                                                                    //Call again jQuery.ready()implement ready Event listening function
    };

} 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();
        }
    };
}

The DOMContentLoaded event is first removed in the function, and then the jQuery.ready() event is invoked. This is the event triggered by the DOM tree (the function that we implemented in readyList.add (FN) in jQuery.fn.ready() will be triggered in turn), as follows:

jQuery.extend({
    isReady: false,
    ready: function( wait ) {                        //Function trigger actually executed ready Event listening function list readyList And ready Event listener function.
        // Either a released hold or an DOMready/load event and not yet ready
        if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) {    //If wait yes true And jQuery.readyWait Equal to 0 or wait No true And jQuery.isReady yes false Then perform initialization jQuery.isReady by false Of
            // 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;                                        //Set up jQuery.inReady by true,Express ready Event is 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 ] );                 //implement ready Event listening function readyList,Context is document(Keyword this),[jQuery]yes ready Parameters for the event listener function.

            // Trigger any bound ready events
            if ( jQuery.fn.trigger ) {
                jQuery( document ).trigger( "ready" ).off( "ready" );
            }
        }
    },
    /*slightly*/
})

writer by: Desert QQ:22969969

Finally, call the readyList.fireWith() method to trigger every function in the list of functions.

Posted by happyme on Thu, 31 Oct 2019 19:26:52 -0700