This article mainly introduces the jQuery-1.9.1 source code analysis series (10) Event System of the Event System Structure of the relevant information, need friends can refer to the next is a major functional point.
Analyze the architecture before analyzing the source code to help you understand the source code. In fact, before jQuery appeared, Dean Edwards's cross-browser AddEvent () design was already excellent, and the design idea of the jQuery Event System was also based on this idea, so let's analyze the event binding of Dean Edwards's predecessors first.
A. jQuery event prototype-Dean Edwards's cross-browser AddEvent () Design
Source code explanation
// Event addition method function addEvent (element, type, handler) {// ensure that each different event response function has only one id if (! Handler. $ guid) handler. $ guid = addEvent. guid ++; // maintain an events attribute for the element and initialize it as an empty object. // Element. the events structure is similar to {"click ":{...}, "dbclick ":{...}, "change ":{...}} if (! Element. events) element. events = {}; // try to retrieve element. in events, the object corresponding to the current event type (this object is more like an array) is assigned to handlers // If element. if no object corresponding to the current event type exists in events, var handlers = element is initialized. events [type]; if (! Handlers) {handlers = element. events [type] ={}; // if this element already has a corresponding event response method, for example, if the onclick method is available, assign the onclick method of element to the 0 element of handlers. The structure of handlers is: // {0: function (e) {...}}, this is why addEvent. the reason for guid Initialization is 1. The reserved space is regarded as 0. // element. the events structure is: {"click": {0: function (e ){...}}, /* omit other event types */} if (element ["on" + type]) {handlers [0] = element ["on" + type];} // store the current event handler in handlers, handler. $ Guid = addEvent. guid ++; addEvent. guid = 1; it must be accumulated from 1. // Therefore, the structure of handlers may be {0: function (e ){...}, 1: function () {}, 2: function () {}and so on ...} handlers [handler. $ guid] = handler; // The following defines a handleEvent function, which is bound to the type event of the element as the event entry. // Note: When clicking an element, the handleEvent function is triggered. The handleEvent function searches for element. events and calls the corresponding function. HandleEvent can be called "Main listening function" element ["on" + type] = handleEvent;}; // counter addEvent. guid = 1; function removeEvent (element, type, handler) {// delete the event handler from the hash table if (element. events & element. events [type]) {delete element. events [type] [handler. $ guid] ;}}; function handleEvent (event) {// compatible with ie event = event | window. event; // this is the node that responds to the event. The vertex has the events attribute (added in addEvent). // obtain the list of event Response functions corresponding to the node. var handlers = this. events [event. type]; // execute for (var I in handlers) in the Loop Response Function list {// keep the correct scope, that is, this keyword this. $ handleEvent = handlers [I]; this. $ handleEvent (event );}};
Repeat the data structure and use an example.
function f0(){...}function f1(){...}function f2(){...}function f3(){...}var dom = document.getElementById("chua");addEvent(dom,"click",f1);addEvent(dom,"change",f1);addEvent(dom,"change",f2);addEvent(dom,"click",f3);addEvent(dom,"change",f3);
After the addEvent () function, the current data structure is:
Element: {onclick: handleEvent (event), // The main listening function onchage: handleEvent (event) of the click event, // The main listening function events: {click: {// This is an array of Classes 0: f0, // element existing Event 1: f1, // subscript 1 is actually f1. $ guid 3: f3 // subscript 3 is actually f3. $ guid. Note that each RESPONSE event has a unique $ guid as the subscript ...}, change :{// This is a class array 1: f1, 2: f2, 3: f3 }}}
The Event System marks $ guid for each response function (namely, the third parameter handler in addEvent (element, type, handler) according to the order in which addEvent is called. Source code
// Ensure that each different event response function has only one id if (! Handler. $ guid) handler. $ guid = addEvent. guid ++;
The $ guid of the final three response functions is
F1. $ guid = 1
F2. $ guid = 2
F3. $ guid = 3
According to the source code
handlers[handler.$$guid] = handler;
The subscript position of a function in any event response function set is fixed. For example, if both the click and change events call f3 as the response event, f3 is in the element. events. click and element. events. the subscript position in change is f3. $ guid = 3; that is, element. events. click [3] = element. events. change [3] = f3.
In this case, assume that an event binding: addEvent (dom, "focus", f3); then, element. events. focus [3] = f3; this is more convenient for objects than arrays. arrays cannot have 3 directly without subscript 0, 1, and 2, but objects can, 3 is an attribute name of the object.
This design has actually taken the shape of the jquery Event System, including the following main features:
1) All events on the element will be saved to the element. events attribute, instead of directly bound to the element. In this way, there can be countless response functions for an event.
2) handleEvent is the "Main listening function" for all events of the element. It manages all functions of the element in a unified manner.
3) all browsers support the element ["on" + type] event binding method, cross-browser compatibility.
Now that we understand the event structure of addEvent, this idea is truly appealing. The following describes the event structure of jQuery.
B. jQuery Event Structure
All function adding events will go to the jQuery. event. add function. This function has two main functions: Adding events and attaching a lot of Event-related information. We directly go to the source code. The source code is similar to how Dean Edwards adds cross-browser compatibility events.
Source code analysis
Add: function (elem, types, handler, data, selector) {var tmp, events, t, handleObjIn, special, eventHandle, handleObj, handlers, type, namespaces, origType, // obtain the cached data of the elem node elemData = jQuery. _ data (elem); // no data or text/comment nodes cannot attach events (but common objects can be appended) if (! ElemData) {return;} // The caller can replace handler if (handler. handler) {handleObjIn = handler; handler = handleObjIn. handler; selector = handleObjIn. selector;} // make sure that the handler function has a unique ID. It will be used later to find/delete this handler function if (! Handler. guid) {handler. guid = jQuery. guid ++;} // if this is the first time you enter, the event structure of the initialization element and the primary Event Response entry if (! (Events = elemData. events) {events = elemData. events ={};} if (! (EventHandle = elemData. handle) {eventHandle = elemData. handle = function (e) {// when the page has been uninstalled after an event is called, discard jQuery. event. the second trigger () event, return typeof jQuery! = Core_strundefined &&(! E | jQuery. event. triggered! = E. type )? JQuery. event. dispatch. apply (eventHandle. elem, arguments): undefined;}; // use elem as a feature of the handle function to prevent memory leakage caused by non-local ie events. eventHandle. elem = elem;} // multiple events are processed using spaces. // For example, jQuery (...). bind ("mouseover mouseout", fn); // core_rnotwhite =/\ S +/g; match the blank character types = (types | ""). match (core_rnotwhite) | [""]; t = types. length; while (t --) {// rtypenamespace =/^ ([^.] *)(? :\. (. +) |) $ // Obtain the namespace and prototype event tmp = rtypenamespace.exe c (types [t]) | []; type = origType = tmp [1]; namespaces = (tmp [2] | ""). split (". "). sort (); // If an event changes its type, use the special event processor to handle the changed event type special = jQuery. event. special [type] | {}; // If the selector has been defined, determine the special event API type. Otherwise, give it a type of type = (selector? Special. delegateType: special. bindType) | type; // update special = jQuery based on the new type. event. special [type] | |{}; // handleObj handles the entire event. handleObj = jQuery. extend ({type: type, origType: origType, data: data, handler: handler, guid: handler. guid, selector: selector, // For use in libraries implementing. is (). we use this for POS matching in 'select' // "needsContext": new RegExp ("^" + whitespace + "* [> + ~] | :( Even | odd | eq | gt | lt | nth | first | last )(?: \ ("+ // Whitespace + "*((? :-\ D )? \ D *) "+ whitespace +" * \) | )(? = [^-] | $) "," I ") // used to determine the intimacy needsContext: selector & jQuery. expr. match. needsContext. test (selector), namespace: namespaces. join (". ")}, handleObjIn); // initialize the event processor queue if (! (Handlers = events [type]) {handlers = events [type] = []; handlers. delegateCount = 0; // non-Custom Event. if the special event processor returns false, only addEventListener/attachEvent if (! Special. setup | special. setup. call (elem, data, namespaces, eventHandle) === false) {// bind the global event if (elem. addEventListener) {elem. addEventListener (type, eventHandle, false);} else if (elem. attachEvent) {elem. attachEvent ("on" + type, eventHandle) ;}}// bind custom events to if (special. add) {special. add. call (elem, handleObj); if (! HandleObj. handler. guid) {handleObj. handler. guid = handler. guid ;}} // Add the event object handleObj to the processing list of the element, and the agent Count increases progressively if (selector) {handlers. splice (handlers. delegateCount ++, 0, handleObj);} else {handlers. push (handleObj);} // trace the event that has been used for event optimization jQuery. event. global [type] = true;} // prevents ie Memory leakage elem = null ;}
Still use instances to describe the event structure of jQuery
《script》 function dohander(){console.log("dohander")}; function dot(){console.log("dot");} $(document).on("click",'#center',dohander) .on("click",'#center',dot) .on("click",dot);《script》
After adding the processing link, the event is added to the element, and the cache data corresponding to the node is also added to the corresponding data. The structure is as follows:
ElemData = jQuery. _ data (elem); elemData = {events: {click: {// Array [3] 0: {data: undefined /{...}, guid: 2, // handler id handler: function dohander (){...}, Namespace: "", needsContext: false, origType: "click", selector: "# center", // selector, used to distinguish different event source types: "click"} 1: {data: undefined /{...}, guid: 3, handler: function dot (){...}, Namespace: "", needsContext: false, origType: "click", selector: "# center", type: "click"} 2: {data: undefined, guid: 3, handler: function dot (){...}, Namespace: "", needsContext: false, origType: "click", selector: undefined, type: "click"} delegateCount: 2, // Number of delegate events, if selector exists, the delegate event length: 3 }}handle: function (e ){...} /* Main event processing entry */{elem: document // features of the handle object }}
The processing of jQuery is similar to that of Dean Edwards's cross-browser compatible events, such as adding guids for each function, storing the RESPONSE event list using the events object, and having a total event processing entry handle.
What improvements have jQuery made?
1) event data is not directly stored on the node, but stored in the jQuery Cache System (internally accessed using the cached jQuery. _ data method)
2) event Delegate: the handler function bound to the current node (in this example, the current node is the document root node) not only contains the current node trigger event (click) event to be processed in response (in this example, the processing function dot corresponding to the selector is undefined), and other nodes (# center node in this example) to trigger the event (click) events to be processed in response (in this example, the selector is the doHandler and dot processing events corresponding to "# center"); the delegation mechanism will be analyzed later.
3) added a lot of data functions, such as namespace: This is mainly used for custom event triggering, such as $ (document ). on ("chua. click ", '# Center', dot), actively trigger $ (" # center "). trigger ("chua. click "). There is additional data: although it is not seen that this is used.
Now the event structure of jQuery is clear. The binding, triggering, and delegation principles of the event will be analyzed later.