Jquery event core source code analysis, jquery event source code

Source: Internet
Author: User

Jquery event core source code analysis, jquery event source code

Starting from binding events, let's look at it step by step:

Take jquery.1.8.3 as an example. The on method is usually used to bind events through jquery, which is roughly divided into the following three types:

$ (Target). on ('click', function () {// function content })
$ (Target). on ('click', '. child', function () {// function content })
$(target).on({    click:function(){},    mouseover:function(){},    mouseout:function(){}})

The first method is our most commonly used method, which binds events directly through the element selector;

The second method is to use the event delegation principle to delegate sub-element events from the original parent element, which is invalid when the first method is used for dynamically added element binding events;

The third is the simple syntax for binding multiple events to the same element at the same time.

Let's take a look at the source code of the on method. If we want to encapsulate a plug-in similar to the on method call, we can write it like on. For details, see another article.JQuery plug-in Development (overflow scrolling)

On: function (types, selector, data, fn,/* INTERNAL */one) {var origFn, type; // Types can be a map of types/handlers // The first parameter is input as the object
        if ( typeof types === "object" ) {            // ( types-Object, selector, data )            if ( typeof selector !== "string" ) { // && selector != null                // ( types-Object, data )                data = data || selector;                selector = undefined;            }            for ( type in types ) {                this.on( type, selector, data, types[ type ], one );            }            return this;        }        if ( data == null && fn == null ) {            // ( types, fn )            fn = selector;            data = selector = undefined;        } else if ( fn == null ) {            if ( typeof selector === "string" ) {                // ( types, selector, fn )                fn = data;                data = undefined;            } else {                // ( types, data, fn )                fn = data;                data = selector;                selector = undefined;            }        }        if ( fn === false ) {            fn = returnFalse;        } else if ( !fn ) {            return this;        }        if ( one === 1 ) {            origFn = fn;            fn = function( event ) {                // Can use an empty set, since event contains the info                jQuery().off( event );                return origFn.apply( this, arguments );            };            // Use same guid so caller can remove using origFn            fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );        }        return this.each( function() {            jQuery.event.add( this, types, fn, data, selector );        });    }

 

We can see that the code in the on method is similar to initialization. By analyzing input parameters, we can correct variables such as type, fn, data, and selector, so as to call jquery correctly. event. add method.Jquery. event is the core of events.

Jquery. eventThe code structure is as follows:

JQuery. event = {add: function () {}, global :{}, remove: function () {}, customEvent: function () {}, trigger: function (){}, dispatch: function () {// In the old version of jquery, this method is named handle}, props: '', fixHooks :{}, keyHooks :{}, mouseHooks :{}, fix: function () {}, special: function () {}, simulate: function (){}}

The add method adds events through some settings for element registration:

Special events refer to events similar to mouseenter, mouseleave, and ready events that are not supported by the browser. They cannot add this event through the unified addEventListener/attachEvent. instead, you can bind and delete events through setup and teardown, as shown below:

beforeunload: {    setup: function( data, namespaces, eventHandle ) {        // We only want to do this special case on windows        if ( jQuery.isWindow( this ) ) {            this.onbeforeunload = eventHandle;        }    },    teardown: function( namespaces, eventHandle ) {        if ( this.onbeforeunload === eventHandle ) {            this.onbeforeunload = null;        }    }}
Add: function (elem, types, handler, data, selector) {var elemData, eventHandle, events, t, tns, type, namespaces, handleObj, handleObjIn, handlers, special; // Don't attach events to noData or text/comment nodes (allow plain objects tho) if (elem. nodeType = 3 | elem. nodeType = 8 |! Types |! Handler |! (ElemData = jQuery. _ data (elem) {return;} // Caller can pass in an object of custom data in lieu of the handler // If the passed handler includes the handler attribute, the handler and selector are set to the correct point using the temporary variables. If (handler. handler) {handleObjIn = handler; handler = handleObjIn. handler; selector = handleObjIn. selector;} // Make sure that the handler has a unique ID, used to find/remove it later // Add a unique guid for each element if (! Handler. guid) {handler. guid = jQuery. guid ++;} // Init the element's event structure and main handler. if this is the first // elemData structure, see the following events = elemData. events; if (! Events) {elemData. events = {}; // initial binding event} eventHandle = elemData. handle; if (! EventHandle) {// eventHandle is processed by dispatch, which is different from the handler elemData. handle = eventHandle = function (e) {// Discard the second event of a jQuery. event. trigger () and // when an event is called after a page has unloaded return typeof jQuery! = "Undefined "&&(! E | jQuery. event. triggered! = E. type )? JQuery. event. dispatch. apply (eventHandle. elem, arguments): undefined;}; // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events eventHandle. elem = elem;} // Handle multiple events separated by a space // jQuery (...). bind ("mouseover mouseout", fn); types = jQuery. trim (hoverHack (types )). split (""); for (t = 0; t <types. length; t ++) {// similar to 'click input KeyUp 'Multiple events are imported at a time. tns = rtypenamespace.exe c (types [t]) | []; type = tns [1]; namespaces = (tns [2] | ""). split (". "). sort (); // If event changes its type, use the special event handlers for the changed type special = jQuery. event. special [type] | {}; // If selector defined, determine special event api type, otherwise given type = (selector? Special. delegateType: special. bindType) | type; // Update special based on newly reset type special = jQuery. event. special [type] | |{}; // handleObj is passed to all event handlers handleObj = jQuery. extend ({type: type, origType: tns [1], data: data, handler: handler, guid: handler. guid, selector: selector, needsContext: selector & jQuery. expr. match. needsContext. test (selector), namespace: Namespaces. join (".")}, handleObjIn); // Init the event handler queue if we're re the first handlers = events [type]; if (! Handlers) {handlers = events [type] = []; handlers. delegateCount = 0; // Only use addEventListener/attachEvent if the special events handler returns false // if the event is not a special event, the addeventListener or attachEvent event is bound. Otherwise, special is preferred. setup binds if (! Special. setup | special. setup. call (elem, data, namespaces, eventHandle) === false) {// Bind the global event handler to the element // the current eventHandle is processed eventHandle if (elem. addEventListener) {elem. addEventListener (type, eventHandle, false);} else if (elem. attachEvent) {elem. attachEvent ("on" + type, eventHandle) ;}} if (special. add) {special. add. call (elem, handleObj); if (! HandleObj. handler. guid) {handleObj. handler. guid = handler. guid ;}// Add to the element's handler list, delegates in front if (selector) {// the element event is the event Delegate handlers. splice (handlers. delegateCount ++, 0, handleObj);} else {// event handlers bound to the element itself. push (handleObj);} console. log (elemData) // Keep track of which events have ever been used, for event optimization jQuery. event. global [type] = true;} // Nullify elem to prevent memory leaks in IE elem = null ;},

Pay attention to the elemData = jQuery. _ data (elem) sentence. We simply bind an event and then look at the elemData value.

$ (Document). click (function (){
Console. log (1)
})

 

As shown in the left figure above, the final result elemData, that is, jquery's cache data, mainly contains two attributes: events and handle. The events includes all the events registered by the current element, such as click and keydown, each event can contain multiple handler and each handler has a unique guid, which will be used to trigger and delete corresponding event functions. The events object also contains a property delegateCount, the total number of proxy events for this element is recorded. The figure on the right shows the handler bound to an event. The proxy event (where the selector part is undefined) is at the top, and the event bound to the element is at the back of the proxy event.

It should be noted that the elem. addEventListener (type, eventHandle, false) in the Code is not equivalent to simply binding the handler processing function, but processing the handler through dispatch.

In addition, in the event function, the first parameter passed in by js is the event object by default.

Next let's take a look at the dispatch method. This method accepts the passed event parameters and processes the events bound to the elements. For example, the Code is as follows:

<Div class = 'parent' style = 'width: 900px; height: 500px; background-color: # CCC '> <p class = 'child'> <a class = 'inner '> click </a> </p> </div> <script> $ ('. parent '). on ('click', function () {}) $ ('. parent '). on ('click ','. child ', function () {}) $ ('. parent '). on ('click ','. inner ', function () {}) </script>

As you can see, the div element is bound with a click event and acts as a proxy for the event of the child element p and a. In this way, when a click event occurs in the div, in the first step, dispatch cyclically traverses the div element from the currentTarget of the event element until it adds the elements and events that need to trigger the event to the handlerQueue array (provided that the element itself has a proxy event ), then, events bound to the element itself are added to handlerQueue. After the preceding two steps, handlerQueue forms a set of events to be triggered. With this set, we can correctly respond to events.

Dispatch: function (event) {// Make a writable jQuery. event from the native event object // use the fix method to handle event compatibility. event = jQuery. event. fix (event | window. event); var I, j, cur, ret, selMatch, matched, matches, handleObj, sel, related, handlers = (jQuery. _ data (this, "events") |{}) [event. type] | []), delegateCount = handlers. delegateCount, args = core_slice.call (arguments), run_all =! Event. exclusive &&! Event. namespace, special = jQuery. event. special [event. type] | |{}, handlerQueue = []; // Use the fix-ed jQuery. event rather than the (read-only) native event args [0] = event; event. delegateTarget = this; // Call the preDispatch hook for the mapped type, and let it bail if desired if (special. preDispatch & special. preDispatch. call (this, event) === false) {return;} // Determine handlers that Shocould run if there are delegated events // Avoid non-left-click bubbling in Firefox (#3861) // right-click bubbling will trigger the click event, but event. the button value is 2 // if delegateCount is not 0, the element itself has a proxy for other element events if (delegateCount &&! (Event. button & event. type = "click") {/* event from event.tar get to the current element # For example, the element itself is bound with event, in addition, it acts as the proxy for child event B and child sub-element c events. # When you click the c element, the execution event sequence is c-B-a, that is, the deeper the node hierarchy, higher event execution priority */for (cur = event.tar get; cur! = This; cur = cur. parentNode | this) {// Don't process clicks (ONLY) on disabled elements (#6911, #8165, #11382, #11764) if (cur. disabled! = True | event. type! = "Click") {selMatch ={}; matches = []; // proxy event. delegateCount indicates the number of proxy events. For the order of different handler events, see the middle-right figure, the proxy event is on, and its own event is under for (I = 0; I <delegateCount; I ++) {handleObj = handlers [I]; sel = handleObj. selector; if (selMatch [sel] === undefined) {selMatch [sel] = handleObj. needsContext? JQuery (sel, this ). index (cur)> = 0: jQuery. find (sel, this, null, [cur]). length;} if (selMatch [sel]) {matches. push (handleObj) ;}} if (matches. length) {handlerQueue. push ({elem: cur, matches: matches}); // delegate event }}// Add the remaining (directly-bound) handlers if (handlers. length> delegateCount) {// handlerQueue of the event. push ({elem: this, matches: handlers. slice (delegateCou Nt)});} // Run delegates first; they may want to stop propagation beneath us // hangdlerQueue is an array of element events and proxy sub-element events. // For example, the html structure is <div> <p> <a> </a> </p> </div>, when the click range is p but not a, the p and div events are executed. // the corresponding handlerQuesu does not contain a for (I = 0; I 

As shown above, the source code has made corresponding remarks. The handlerQueue structure is as follows. The first two items are proxy events, the last one is the event of the element itself, and matches is the handler set of the current element.

 

The fix function is used to modify the event object. First, a new extensible event object is constructed. the event also contains props, fixHooks, keyHooks, and mouseHooks, which respectively store the public attributes, keyboard event attributes, and mouse event attributes of the event object, assign new attributes to the newly constructed event object based on the event type, and also assign custom attributes to the event object during later extension.

fix: function( event ) {    if ( event[ jQuery.expando ] ) {        return event;    }    // Create a writable copy of the event object and normalize some properties    var i, prop,        originalEvent = event,        fixHook = jQuery.event.fixHooks[ event.type ] || {},        copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;    event = jQuery.Event( originalEvent );    for ( i = copy.length; i; ) {        prop = copy[ --i ];        event[ prop ] = originalEvent[ prop ];    }    // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2)    if ( !event.target ) {        event.target = originalEvent.srcElement || document;    }    // Target should not be a text node (#504, Safari)    if ( event.target.nodeType === 3 ) {        event.target = event.target.parentNode;    }    // For mouse/key events, metaKey==false if it's undefined (#3368, #11328; IE6/7/8)    event.metaKey = !!event.metaKey;    return fixHook.filter? fixHook.filter( event, originalEvent ) : event;},

Of course, jquery. event has other methods such as trigger, remove, and simulate, which are not listed here. The basic ideas are the same. After understanding the above principles, you can expand the jquery method as needed, such as the mousewheel event. We can use the fix method to expand the event object, instead of writing a set of compatible code, you can analyze the code in the next section.

If there are any errors or mistakes in this article, please point them out in time. Thank you!

The jquery version used in this article is 1.8.3. 1.2.6 jquery event core is easier to understand. Of course, there is no event proxy processing in it.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.