This article mainly introduces the jQuery1.9.1 source code analysis series (10) Event System's active event triggering and Simulation of bubble processing related information. For more information, see the following:
stopPropagation: function() { var e = this.originalEvent; ... if ( e.stopPropagation ) { e.stopPropagation(); }
JQuery overload the stopPropagation function of the local event object called by the stopPropagation function to prevent bubbling. That is to say, the current node rather than the event source is blocked from bubbling.
When it comes to triggering events, our first response is to use $ (...). click () to trigger the click event. This method is undoubtedly concise and clear. If you can use this method, we recommend this method. But what if it is a custom event? For example, define a $ (document). on ("chuaClick", "# middle", fn); how to trigger an event in this case? $ ("# Middle"). trigger ("chuaClick") is required.
A. trigger the event low-level API -- jQuery. event. trigger
The trigger function supports trigger of all types of events. These events are divided into two types: Common Browser events (including namespace events such as "click. chua") and custom events. Because unified processing is required, the function's internal implementation does not call the. click () method to perform shortcuts to Common Browser events, but to unify the process. The process is as follows:
1. Get the event to be triggered (the input event may be an event type rather than an event object)
event = event[ jQuery.expando ] ? event :new jQuery.Event( type, typeof event === "object" && event );
2. Modify browser events (mainly correction event sources) and combine the correct event processing parameter data
If (type. indexOf (". ")> = 0) {// trigger an event with a namespace. First, retrieve the event type used by the event processing entry function handle (). type namespaces = type. split (". "); type = namespaces. shift (); namespaces. sort ();}... // The caller can pass jQuery. event object, common object, or even string event = event [jQuery. expando]? Event: new jQuery. event (type, typeof event === "object" & event); event. isTrigger = true; event. namespace = namespaces. join (". "); event. namespace_re = event. namespace? New RegExp ("(^ | \.)" + namespaces. join ("\\.(? :. *\\. |) ") + "(\\. | $) "): Null; // reset the result attribute to avoid the last result event. result = undefined; if (! Event.tar get) {event.tar get = elem;} // clone the passing parameter data and put the event in front of the passing parameter data, and create a list of parameters of the event processing entry function, after creation, the result may be [event, data] data = null? [Event]: jQuery. makeArray (data, [event]);
The later part of the combined event processing parameter list data is called during subsequent processing
if ( handle ) { handle.apply( cur, data ); }
3. Special events for determining whether a special Node object is a special event.
special = jQuery.event.special[ type ] || {}; if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { return; }
There are few special events to be processed here.
Special: {click. trigger: function () {// checkbox, which triggers a local event to ensure that the status is correct if (jQuery. nodeName (this, "input") & this. type = "checkbox" & this. click) {this. click (); return false ;}}, focus. trigger: function () {// triggers a local event to ensure that the defocusing/focusing sequence is correct if (this! = Document. activeElement & this. focus) {try {this. focus (); return false;} catch (e) {// Support: IE <9 // If we error on focus to hidden element (#1486, #12518 ), // let. trigger () run the handlers }}, blur. trigger: function () {if (this = document. activeElement & this. blur) {this. blur (); return false ;}}}
4. traverse the parent node from the event source until the Window object, save (save to eventPath) The passed node to the backup
For (; cur = cur. parentNode) {eventPath. push (cur); tmp = cur;} // press the window into eventPath (e.g ., it is neither a common object nor a disconnected DOM) if (tmp = (elem. ownerDocument | document) {eventPath. push (tmp. defaultView | tmp. parentWindow | window );}
5. loop through the previously saved nodes and access the node cache. If there is a corresponding event type processing queue, retrieve the bound events (entry function) for calling.
// JQuery binding function processing: determines whether the corresponding event processing function is saved in the node cache. If yes, execute handle = (jQuery. _ data (cur, "events") |{}) [event. type] & jQuery. _ data (cur, "handle"); if (handle) {handle. apply (cur, data) ;}// handle local binding handle = ontype & cur [ontype]; if (handle & jQuery. acceptData (cur) & handle. apply & handle. apply (cur, data) ===false) {event. preventDefault ();}
6. Finally, handle browser default events, such as the submission form processing of the submit tag.
// If no one stops the default processing, execute if (! OnlyHandlers &&! Event. isDefaultPrevented ()){...}
Note: adding a namespace to a common event is still a normal event, and the normal call method still works. For example, $ (document ). on ('click. chua ', "# id", fn1 ). on ("click", "# id", fn2); when you click the "# id" node, fn1 is still called. The only way to trigger a specified namespace event is trigger: $ ("# id"). trigger ("click. chua"). At this time, only fn1 is called.
In steps 4th and 5, we can see another major role of the trigger-simulating bubble processing. It will be analyzed later
B. special event handling jQuery. event. special (mainly event substitution and bubble simulation)
The delegated design is event-based and can be bubbling. However, some events cannot be bubbling, and some events support different bubbling situations on different browsers. The event types supported by different browsers are also different. These processes are mainly stored in jQuery. event. special. The jQuery. event. special object stores the variables and methods required to adapt to a specific event.
Specifically:
DelegateType/bindType (used to adjust the event type)
Setup (called when an event is bound for the first time)
Add (called during event binding)
Remove (called when events are unbound)
Teardown (called when all event bindings are unbound)
Trigger (called during internal trigger events)
NoBubble
_ Default
Handle (called when an event is actually triggered)
PreDispatch (called before the event is actually triggered)
PostDispatch (called after the event is actually triggered)
Let's take a look at the simulation function simulate.
Simulate: function (type, elem, event, bubble) {// construct a new event to differentiate previously bound events. // newly built events avoid blocking bubbling, but if simulated events can block default operations, we will block the default operations. Var e = jQuery. extend (new jQuery. event (), event, {type: type, isSimulated: true, originalEvent :{}}); if (bubble) {jQuery. event. trigger (e, null, elem);} else {jQuery. event. dispatch. call (elem, e);} if (e. isDefaultPrevented () {event. preventDefault ();}}
No. The jQuery. event. trigger function is used to simulate the bubble function.
Special Group 1
This involves the problem of bubble processing.
Special: {load: {// block image triggering. load event bubbles to window. load noBubble: true}, click: {// when the checkbox is triggered, make sure that the trigger status is correct. trigger: function () {if (...) {this. click (); return false ;}}, focus: {// trigger the current node blur/focus event to ensure that the queue is correctly trigger: function () {if (this! = Document. activeElement & this. focus) {try {this. focus (); return false;} catch (e) {// IE <9. If we mistakenly get the focus of a hidden node (#1486, #12518 ), // Let. trigger () running processor }}, delegateType: "focusin"}, blur: {trigger: function () {if (this = document. activeElement & this. blur) {this. blur (); return false ;}}, delegateType: "focusout"}, beforeunload: {postDispatch: function (event) {// even the returnValue In undefined, Firefox still displays a warning if (event. result! = Undefined) {event. originalEvent. returnValue = event. result ;}}}}
Focus/blur is not bubble, but we can still bind it through $ (document). on ('focal ',' # left ', fn). How does it do it? Let's look at jQuery's processing.
Step 1: bind the focus event to focusin. focusin is bubbling in W3C standards, browsers other than Firefox do also support bubbling (focusin/focusout will be detailed later)
Type = (selector? Special. delegateType: special. bindType) | type;
Then, obtain the new special based on the new type (focusin ).
Special = jQuery. event. special [type] || {};
The obtained special result is
JQuery. each ({focus: "focusin", blur: "focusout"}, function (orig, fix) {var attaches = 0, handler = function (event) {// simulate bubble jQuery. event. simulate (fix, event.tar get, jQuery. event. fix (event), true) ;}; jQuery. event. special [fix] = {setup: function () {if (attaches ++ = 0) {document. addEventListener (orig, handler, true) ;}}, teardown: function () {if (-- attaches === 0) {document. removeEventListener (orig, handler, true );}}};});
Then, bind the event. In fact, the binding event is compatible with focusin and focusout. the first judgment in the source code is special. setup. call (...) According to the preceding setup function, the first entry is actually the document in the setup function. addEventListener (orig, handler, true) binds events. Note: The first parameter is orig. Because Firefox does not support focusin/focusout, jQuery uses focus/blur instead to listen for events; note that the third parameter is true, indicating that the event is triggered in the event capture phase.
We know that any browser captures nodes from the outer layer to the precise node, and all the focusin events will be captured, and then execute the handler function (which contains jQuery. event. simulate function, source code omitted ). Bind other events to the if branch to bind the events directly to elem.
if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { if ( elem.addEventListener ) { elem.addEventListener( type, eventHandle, false ); } else if ( elem.attachEvent ) { elem.attachEvent( "on" + type, eventHandle ); }}
Special Group 2: mouseenter/mouseleave
// Use mouseover/out and event timing to detect the creation of the mouseenter/leave event jQuery. each ({mouseenter: "mouseover", mouseleave: "mouseout"}, function (orig, fix) {jQuery. event. special [orig] = {delegateType: fix, bindType: fix, handle: function (event) {var ret, target = this, related = event. relatedTarget, handleObj = event. handleObj; // For mousenter/leave, handler is called only when related is outside the target. // reference: When the mouse leaves/enters the browser window, there is no relatedTarget. If (! Related | (related! = Target &&! JQuery. contains (target, related) {event. type = handleObj. origType; ret = handleObj. handler. apply (this, arguments); event. type = fix;} return ret ;}};});
Note that the mouseenter event is triggered only when the mouse pointer passes through the selected element. For mouseleave, The mouseenter sub-element does not trigger events repeatedly; otherwise, flashes often occur in IE.
Using mouseover/out and event timing detection to create a mouseenter/leave event has a key judgment
if ( !related || (related !== target && !jQuery.contains( target, related )) )
Here! JQuery. contains (target, related) indicates that the related is outside the target. We use legends to explain
Assume that the mouseenter event is handled and the target is entered.
The mouse moves from related to target, and it is obvious that the related is outside the target. When the mouse moves to the target, the condition is met and processing is called.
Now, in turn, it is obvious that the related is in the target, so the mouse is in the mouseenter state (meaning that the mouseenter processor is processed before ), to avoid repeated calls, of course, it is returned without any processing.
Assume that the mouseleave event is handled and the target is left.
The mouse moves from target to related, and it is obvious that the related is in the target. Therefore, when the mouse moves to related, it still leaves the target without processing.
The mouse moves from target to related, and it is obvious that the related is outside the target. Therefore, when the mouse moves to related, it has left the target range for processing.
Special Group 3: submit and change
The main reason is that submit cannot be bubbling in ie.
JQuery. event. special. submit mainly has the following features:
Setup
PostDispatch
Teardown
According to the Code for adding an event, if the event meets the conditions, the system will call setup to add the event.
If (! Special. setup | special. setup. call (elem, data, namespaces, eventHandle) === false)
In ie, jQuery simulates submit events and replaces them with click and keypress, except that namespace is added to distinguish them from normal click and keypress events.
setup: function() { ... jQuery.event.add( this, "click._submit keypress._submit", function( e ) { var elem = e.target, form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; if ( form && !jQuery._data( form, "submitBubbles" ) ) { jQuery.event.add( form, "submit._submit", function( event ) { event._submit_bubble = true; }); jQuery._data( form, "submitBubbles", true ); } });},
During the event calling process (dispatch), it will call postDispatch for processing.
If (special. postDispatch) {special. postDispatch. call (this, event);} call simulate in postDispatch to complete event processing postDispatch: function (event) {// If form was submitted by the user, bubble the event up the tree if (event. _ submit_bubble) {delete event. _ submit_bubble; if (this. parentNode &&! Event. isTrigger) {jQuery. event. simulate ("submit", this. parentNode, event, true );}}},
Teardown is used to delete event binding
The handling of change events in ie is similar to that in submit. The events are monitored using beforeactivate instead. The processing function is changed to handle and the code is executed in the event distribution (dispatch ).
ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) .apply( matched.elem, args );
The main source code is as follows:
JQuery. event. special. change = {setup: function () {// rformElems =/^ (?: Input | select | textarea) $/I if (rformElems. test (this. nodeName) {// IE will not trigger the change event before check/radio is deprecated; it will trigger its click Event After attribute change // in special. change. handle will swallow the change event triggered by defocusing. // here, the onchange event will be triggered after check/radio is out of focus. if (this. type = "checkbox" | this. type = "radio") {jQuery. event. add (this, "propertychange. _ change ", function (event) {if (event. originalEvent. propertyName = "checked") {this. _ just_cha Nged = true ;}}); jQuery. event. add (this, "click. _ change", function (event) {if (this. _ just_changed &&! Event. isTrigger) {this. _ just_changed = false;} // Allow triggered, simulated change events (#11500) jQuery. event. simulate ("change", this, event, true) ;});} return false ;}// event proxy; add a change event to the input node of the descendant to process jQuery. event. add (this, "beforeactivate. _ change ", function (e) {var elem = e.tar get; if (rformElems. test (elem. nodeName )&&! JQuery. _ data (elem, "changeBubbles") {jQuery. event. add (elem, "change. _ change ", function (event) {if (this. parentNode &&! Event. isSimulated &&! Event. isTrigger) {jQuery. event. simulate ("change", this. parentNode, event, true) ;}}; jQuery. _ data (elem, "changeBubbles", true) ;}) ;}, handle: function (event) {var elem = event.tar get; // swallow the change event of the local ticket and check box. We have set the event if (this! = Elem | event. isSimulated | event. isTrigger | (elem. type! = "Radio" & elem. type! = "Checkbox") {return event. handleObj. handler. apply (this, arguments );}},}
OK. Now, the event system has a paragraph. Thank you for your support.