JQuery 1.9.1 Source Analysis Series (10) Event system active triggering event and simulated bubbling processing _jquery

Source: Internet
Author: User

Found a small point that had not been noticed before

   Stoppropagation:function () {
      var e = this.originalevent;
      ...
      if (e.stoppropagation) {
        e.stoppropagation ();
      }

The Stoppropagation function of the local event object called by the jquery overload stoppropagation function blocks bubbling. In other words, the current node, not the event source, is blocked from bubbling.

When it comes to triggering events, our first reaction is to use $ (...). Click () This way triggers the Click event. There is no doubt that this approach is straightforward, if it is recommended in this way. But what if it's a custom event? For example, define a $ (document). On ("Chuaclick", "#middle", FN); How does this trigger an event? This will take $ ("#middle"). Trigger ("Chuaclick").

A. Triggering events low-api--jquery.event.trigger

The trigger function provides support for triggering all types of events. These events fall into two main categories: normal browser events (which include events with namespaces such as "Click.chua"), and custom events. The internal implementation of the function is not invoked because it is to be unified. Click () is a way to make shortcuts to ordinary browser events, but to unify the process. The processing process is as follows

  1. Gets the event to be triggered (the incoming 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. Fix browser events (mainly with modified event source) and combination of correct event handling parameters data

  if (Type.indexof (".") >= 0) {
        //Event trigger with a namespace, the event-handler entry function handle () is used for the type
        namespaces = Type.split (".").
        type = Namespaces.shift ();
        Namespaces.sort ();
      }
      ...//Can the caller pass the Jquery.event object, the ordinary object, or even the 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;
      Resets the result property to avoid the last residual
      event.result = undefined;
      if (!event.target) {
        event.target = elem;
      }
      To clone the parameter data and place the event before the reference data, create an argument list of the event-handling entry function, which may result in [event,data]
      data = data = null?
      [Event]:
      jquery.makearray (data, [Event]);

The following combination event processing argument list data is called when processed later

  if (handle) {
          handle.apply (cur, data);
        }

 3. A special event that determines whether a particular node object is special

Special = jquery.event.special[Type] | |  {};
if (!onlyhandlers && special.trigger && special.trigger.apply (elem, data) = = False) {return
;
}

There are fewer incidents that require special treatment here.

 Special: {
      click.trigger:function () {   //checkbox, triggering local event to ensure correct state if (Jquery.nodename (this, "input") && t His.type = = "checkbox" && this.click) {
            this.click ();
            return false;
          }
      ,
      focus.trigger:function () {  //triggers a local event to guarantee that the Defocus/focus 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. Trigge R () Run the Handlers
            }
          }
      ,
      blur.trigger:function () {if (this = = Document.activeelement & & This.blur) {
            this.blur ();
            return false;
          }
      }
}

4. Start traversing the parent node from the event source until the Window object, save the passed node (save to Eventpath) and stand down

for (; cur; cur = cur.parentnode) {
eventpath.push (cur);
TMP = cur;
}
Press window also into eventpath (e.g., not normal object nor disconnected DOM)
if (tmp = = (Elem.ownerdocument | | document)) {
Eventpath.push (Tmp.defaultview | | tmp.parentwindow | | window);
}

  5. Loop the previously saved node, access the node cache, and call out its bound event (entry function) if there is a corresponding event type processing queue.      

JQuery binding function Processing: To determine whether the node cache to save the appropriate event handler functions, if there is the execution
       handle = (Jquery._data (cur, "events") | | {}) [Event.type] && jquery._data (cur, "handle");
       if (handle) {
           handle.apply (cur, data);
       }
       Local binding processing
       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 submission form processing for submit tags.

  If no one is blocking the default processing, the execution of
      if (!onlyhandlers &&!event.isdefaultprevented ()) {
        ...
      }

  Note: Ordinary events plus namespaces still belong to ordinary events, and the normal invocation still works. such as $ (document). On (' Click.chua ', ' #id ', fn1). On ("click", "#id", fn2); Fn1 will still be invoked when clicking on the "#id" node. The only way to trigger the specified namespace event is trigger:$ ("#id"). Trigger ("Click.chua"), at which point only fn1 is invoked.

From the 4th and 5 steps you can see another huge effect of trigger--simulated bubbling processing. The following will analyze

B. Event special handling JQuery.event.special (main event substitution, simulation bubbling) detailed

The delegate design is based on events that can be bubbling. But some events are not bubbling, and some events support different bubbles in different browsers. There are also different types of events supported by browsers. These processes are mostly placed in jQuery.event.special. The JQuery.event.special object holds the variables and methods needed to fit a specific event.

Specifically:

Delegatetype/bindtype (for event type adjustment)
Setup (called at the time of the first binding of an event)
Add (called at event binding)
Remove (called at time of unblocking event binding)
Teardown (called when all event bindings are dismissed)
Trigger (called at time of Internal trigger event)
Nobubble
_default
Handle (called when the event is actually triggered)
Predispatch (called before actual triggering event)
Postdispatch (called after the event is actually triggered)

Take a look at the function that simulates bubbling simulate

Simulate:function (type, Elem, event, bubble) {
      //Build a new event to distinguish between previously bound events.
      The newly built event avoids bubbling, but if the impersonation event can block the default action, we do the same block default action.
      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 ();
      }
    }
   

See no, really simulate bubble function is JQuery.event.trigger function

Special First Group

This involves the bubbling process.

Special: {load: {//Prevent triggering image.load event bubbling to Window.load nobubble:true}, click: {//checkbox is guaranteed to be in correct state when triggered Trigger:function () {if (...)
  {This.click (); return false;}} Focus: {//trigger this current node Blur/focus event to ensure that the queue is correct trigger:function () {if (This!== document.activeelement && Amp
          This.focus) {try {this.focus ();
        return false;
    catch (E) {//ie<9, if we mistakenly let the hidden node get the focus (#1486, #12518),//Let. Trigger () run the processor} }, Delegatetype: "Focusin"}, Blur: {trigger:function () {if (this = = Document.activeelement && Amp
        This.blur) {This.blur ();
      return false; }, Delegatetype: "Focusout"}, Beforeunload: {postdispatch:function (event) {//even returnvalue, etc. The Undefined,firefox still displays a warning if (event.result!== undefined) {event.originalEvent.returnValue = Event.resul
      T }
    }
  }
}

Focus/blur is not supposed to be bubbling, but we can still go through $ (document). On (' Focus ', ' #left ', FN) to bind, how do we do that? Let's see how jquery is handled.

In the first step, the focus-bound event is converted to Focusin to bind, Focusin is bubbling in the standard of the consortium, and the browser outside of Firefox does support bubbling (the Firefox browser focusin/focusout support bubbling compatibility will be detailed later)

Type = (selector special.delegateType:special.bindType) | | Type

The new special is then obtained based on the newly obtained type (focusin)

Special = jquery.event.special[Type] | | {};

The special result obtained is

Jquery.each ({focus: "Focusin", Blur: "Focusout"}, Function (orig, fix) {
var attaches = 0,
handler = Functi  On (event) {
//simulate bubbling
jQuery.event.simulate (fix, Event.target, 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.removeeventlist  Ener (orig, Handler, true);
          }
};
});

Then, is the binding event, binding events in fact on the Focusin, focusout do a compatible processing, the first source of judgment has Special.setup.call (... This code, when the first entry is visible according to the setup function above, is actually binding events through the Document.addeventlistener (orig, Handler, true) in the Setup function, noting that the first argument is orig, Because Firefox does not support Focusin/focusout, jquery uses Focus/blur instead to listen for events; Note that the third argument is true, indicating that the event was triggered during the event capture phase.

We know that any browser capture is from the outer layer to the exact node, all the Focusin events will be captured, and then execute the handler function (inside the JQuery.event.simulate function, the source code). Other event bindings Enter the IF branch to bind the event directly to the Elem

if (!special.setup | | special.setup.call (elem, data, namespaces, eventhandle) = = False) {
if (elem.addeventlis  Tener) {
Elem.addeventlistener (type, eventhandle, false); 
else if (elem.attachevent) {
elem.attachevent ("on" + Type, eventhandle);
}
}

Special Group II: Mouseenter/mouseleave

Create Mouseenter/leave event Jquery.each using Mouseover/out and event timing detection
({
  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 = even T.handleobj;
        For Mousenter/leave, the handler/reference is invoked when related is outside target
        : 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 crosses the selected element. Corresponding mouseleave in this way, mouseenter elements do not repeatedly trigger events, otherwise, in IE, there is often flicker occurs

Using Mouseover/out and event timing detection to create a Mouseenter/leave event has a critical judgment

if (!related | | (Related!== target &&!jquery.contains (target, related))

Of these,!jquery.contains (target, related) indicates that related is outside target. We use the legend to explain

We assume that we are dealing with the MouseEnter event and entering target.

The mouse from related to target, obviously related outside target, so when the mouse moves to target, the condition is met and the call is processed.

Now, in turn, it is obvious that related is in target, then the mouse is in the MouseEnter state (which means that the MouseEnter processor has been processed before), and avoiding repeated calls is of course not directly returned without any processing.

  

We assume that the MouseLeave event is handled, leaving target.

Mouse from Target to related, obviously related in target, so when the mouse moved to the related still do not leave target, do not handle.

  

Mouse from Target to related, obviously related outside target, so when the mouse moved to related has left the target range, do processing.

  

Special Third Group: Submit and change

The main is IE under submit cannot bubble processing

  JQuery.event.special.submit mainly has a few characteristics

Setup
Postdispatch
Teardown

Depending on the code that adds the event, when the event is added, Setup is invoked to add the event if it meets the criteria

if (!special.setup | | special.setup.call (elem, data, namespaces, eventhandle) = = False)

jquery simulates the submit event under IE to click and keypress instead, except to add namespaces to differentiate and normal click and KeyPress events.

Setup:function () {
...
JQuery.event.add (This, "Click._submit keypress._submit", function (e) {
var elem = e.target,
form = Jque Ry.nodename (Elem, "input") | | Jquery.nodename (Elem, "button")?      elem.form:undefined;
if (Form &&!jquery._data (form, "Submitbubbles")) {
jQuery.event.add (form, "Submit._submit", func        tion (event) {
event._submit_bubble = true;
});
Jquery._data (Form, "Submitbubbles", true);
}}
);

Postdispatch is invoked during the event invocation (dispatch) to process the

if (special.postdispatch) {
    Special.postDispatch.call (this, event);
}
Postdispatch Simulate Completion event processing
postdispatch:function (event) {
//If form is submitted by the user, bubble the    Event up of the tree
if (event._submit_bubble) {
delete event._submit_bubble;
if (This.parentnode &&!event.istrigger) {
jQuery.event.simulate ("Submit", This.parentnode, event, tr    UE);
}
}
,

Teardown used in Delete event bindings

The processing of the change event in IE is similar to that of submit, the event uses beforeactivate substitution to listen, the processing function becomes handle, executes the code 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 does not trigger the Change event before the Check/radio is lost; The Click event that triggers it after the property has changed//in spec
          The change event triggered by the Ial.change.handle will be swallowed.//It will trigger the onchange event after the Check/radio is lost. if (This.type = = "checkbox" | | this.type = = "Radio") {JQuery.event.add (this, "Propertychange._change", F Unction (Event) {if (Event.originalEvent.propertyName = = "Checked") {This._just_change
              D = true;
            }
            }); JQuery.event.add (This, "Click._change", function (event) {if (this._just_changed &&!event.istri
              Gger) {this._just_changed = false; }//Allow triggered, simulated change events (#11500) jQuery.event.simulate (' Change ', this,
            event, True);
          }); } rEturn false; }//Event agent; Lazy mode adds change event handling to descendant input nodes JQuery.event.add (this, "Beforeactivate._change", function (e) {var elem =
          E.target; if (Rformelems.test (elem.nodename) &&!jquery._data (Elem, "Changebubbles")) {JQuery.event.add ( Elem, "Change._change", function (event) {if (This.parentnode &&!event.issimulated &&!e
              Vent.istrigger) {jQuery.event.simulate ("change", This.parentnode, event, true);
            }
            });
          Jquery._data (Elem, "Changebubbles", true);
      }
        });
        }, Handle:function (event) {var elem = Event.target; Swallow the Change event for the local Radio box and check box, we've already set out on top. Event if (this!== elem | | event.issimulated | | event.istrigger | | (Elem.type!== "Radio" && elem.type!== "checkbox"))
        {return event.handleObj.handler.apply (this, arguments); } },     }

OK, to this, the event system also tells a paragraph, thank you for your support.

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.