The basic concept

Event delegate, in layman’s terms, is an element that responds to an event (click, keydown……) Delegate to another element;

Will, in general, the elements of one or a set of events entrusted to its parent layer or outer element, real binding event is outer element, when the incident response to need binding elements, through the event bubbling mechanism bind the event to trigger its outer elements, and then in the outer elements to perform the function.

For example, one way is that the students in the same dormitory receive the parcel at the same time. Another way is to entrust the matter to the dormitory leader and ask one person to go out and take all the parcel and then distribute it to each student according to the recipient.

Here, taking delivery is an event, and each student refers to the DOM element that needs to respond to the event, while the dormitory leader who goes out to get the delivery is the agent element, so the element that really binds the event is this element. The process of delivering the delivery according to the recipient is in the event execution. You need to determine which or more of the proxied elements the currently responding event should match.

The event bubbling

As mentioned earlier, the implementation of event delegation in the DOM utilizes the mechanism of event bubbling. What is event bubbling?

AddEventListener in the document. We can set up the event model: event bubbling, event capture, in general are in event bubbling model;

As shown in the figure above, the event model refers to three stages:

  • Capture phase: In an event bubbling model, the capture phase does not respond to any events;
  • Target phase: The target phase refers to the event response to the lowest element that triggers the event;
  • Bubbling stage: in the bubbling stage, the triggering response of the event extends from the bottom target layer by layer to the outermost layer (root node). Event proxy binds the event that needs to be responded by the inner layer to the outermost layer by using the mechanism of event bubbling. # # #

Advantages of delegation

1. Reduce memory consumption

Imagine if we had a list with a large number of list items, and we needed to respond to an event when we clicked on the list item;

<ul id="list"> <li>item 1</li> <li>item 2</li> <li>item 3</li> ...... <li>item n</li> </ul> // ...... I have li in the middleCopy the code

If you bind a function to each list item one by one, it is very expensive in memory, and requires a lot of performance in efficiency;

Therefore, it is better to bind the click event to its parent layer, ul, and then match the target element when the event is executed.

So event delegation can reduce a lot of memory consumption and save efficiency.

2. Dynamically bind events

For example, in the above example, there are only a few list items, and we bind events to each list item;

In many cases, we need to add or remove list items dynamically through AJAX or user operations, so every time we change, we need to re-bind events to the new elements, and unbind events to the elements to be deleted.

This is not the case with event delegates, because events are bound to the parent layer and are not related to the increase or decrease of the target element. Execution to the target element is matched in response to the actual execution of the event function.

So using events can reduce a lot of rework in the case of dynamically binding events.

Event delegate in jQuery

The event delegate in jQuery is probably used by many of you. There are several methods to implement it:

  • * *. On ∗ ∗ : basic usage: on * * : basic usage: on ∗ ∗ : basic usage: (‘ parent ‘) on (‘ click ‘, ‘a’, function () {the console. The log (‘ the click event on the tag a ‘); $(‘.parent’);}), the event of the element below the.parent element is represented by $(‘.parent’);

  • **. Delegate ** **. Delegate ** * .delegate∗ :(‘.parent’).delegate(‘a’, ‘click’, function () {console.log(‘click event on tag a’); }), same as above, and there is a corresponding $. Delegate to delete the delegate event;

  • **. Live ∗: Basic use method :. Live **: Basic use method: . Live ∗ ∗ : basic method of use: (‘ a ‘(‘ parent’)). The live (‘ click ‘, function () to the console. The log (‘ clickeventontaga ‘);) Live (‘click’, function () {console.log(‘click event on tag a’); }), same as above, however if no parent element (‘.parent ‘) is passed).live(‘ click ‘,function()console.log(‘ clickeventontaga ‘);) If no parent element (.parent) is passed, the event is delegated to $(document) by default; (Abolished)

Realize the function

Basic implementation

Let’s say we have an HTML fragment like this:

<ul id="list"> <li>item 1</li> <li>item 2</li> <li>item 3</li> ...... <li>item n</li> </ul> // ...... I have li in the middleCopy the code

Delegate the event delegate of the li element in #list to its parent element:

// Bind the event document.getelementById ('list').addeventListener ('click', Function (e) {/ / compatibility with var event = e | | window. The event; var target = event.target || event.srcElement; / / determine whether match the target element if (target. The nodeName. ToLocaleLowerCase = = = 'li') {the console. The log (' the content is: ', target. The innerHTML); }});Copy the code

In the code above, the target element is the element clicked below the #list element, and the target attributes (such as nodeName, ID, etc.) can be used to more accurately match a type of #list li element.

Use element.matches for exact matches

If you change the HTML to:

<ul id="list"> <li className="class-1">item 1</li> <li>item 2</li> <li className="class-1">item 3</li> ...... <li>item n</li> </ul> // ...... I have li in the middleCopy the code

Here, we want to delegate the click event delegate of the li element under the #list element (and its class is class-1) to the #list element;

If we need by using the method of the above in the if (target. The nodeName. ToLocaleLowerCase = = = ‘li’) judgment in a judgment of target. The nodeName. ClassName = = = ‘class 1’.

But it’s too much of a judgment call to make CSS matches as flexible as it could be. Use the Element.matches API to match.

Using the Element. Matches API: Element.matches(selectorString), selectorString is both a CSS selector rule, such as target.matches(‘li.class-1’), which returns a Boolean value, Return true if the target element is the tag Li and its class is class-1, false otherwise;

Of course, there are some compatibility issues, requiring a modern browser version of IE9 or higher;

We can use Polyfill to solve compatibility problems:

if (! Element.prototype.matches) { Element.prototype.matches = Element.prototype.matchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector || Element.prototype.webkitMatchesSelector || function(s) { var matches = (this.document || this.ownerDocument).querySelectorAll(s), i = matches.length; while (--i >= 0 && matches.item(i) ! == this) {} return i > -1; }; }Copy the code

Add element. matches to fulfill our requirements:

if (! Element.prototype.matches) { Element.prototype.matches = Element.prototype.matchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector || Element.prototype.webkitMatchesSelector || function(s) { var matches = (this.document || this.ownerDocument).querySelectorAll(s), i = matches.length; while (--i >= 0 && matches.item(i) ! == this) {} return i > -1; }; } the document. The getElementById (' list '.) addEventListener (' click ', function (e) {/ / compatibility with var event = e | | window. The event; var target = event.target || event.srcElement; if (target.matches('li.class-1')) { console.log('the content is: ', target.innerHTML); }});Copy the code

Function encapsulation

For more scenarios we can encapsulate the functionality of the event broker into a common function that can be reused. Use the above example to implement a function eventDelegate that takes four parameters:

  • [String] A selector String used to filter the parent elements that need to implement the proxy, both events that need to be actually bound;
  • [String] A selector String used to filter the descendants of the selector elements that trigger the event.
  • [String] One or more space-separated event types and optional namespaces, such as click or keydown.click;
  • [Function] A Function that requires a proxy event response;

Here are a few key points:

  • There may be multiple elements for the parent layer of the proxy, requiring one by one binding events;
  • There may be multiple bound event types, so you need to bind events one by one.
  • Compatibility issues need to be considered in dealing with matching proxied elements;
  • You need to pass in the correct arguments and take this into account when executing the bound function;
function eventDelegate (parentSelector, targetSelector, events, Foo) {/ / function to be executed by the trigger function triFunction (e) {/ / compatibility with var event = e | | window. The event; var target = event.target || event.srcElement; // Handle matches compatibility if (! Element.prototype.matches) { Element.prototype.matches = Element.prototype.matchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector || Element.prototype.webkitMatchesSelector || function(s) { var matches = (this.document || this.ownerDocument).querySelectorAll(s), i = matches.length; while (--i >= 0 && matches.item(i) ! == this) {} return i > -1; }; If (target.matches(targetSelector)) {// perform the binding function, Note this foo. Call (target, Array. Prototype. Slice. The call (the arguments)); Events.split ('.').foreach (function (evt) {// Multiple parent elements need to be individually bound Array.prototype.slice.call(document.querySelectorAll(parentSelector)).forEach(function ($p) { $p.addEventListener(evt, triFunction); }); }); }Copy the code

To optimize the

When the propped element is not the target element, and the element targeted by the targetSelector is not an event. Target, Target’s parentNode to match targetSelector, until parentSelector.

Such as:

<ul id="list">
  <li><span>item 1</span></li>
  <li><span>item 2</span></li>
</ul>
Copy the code

Target refers to Li span, so you need to iterate through the outer elements until you reach the function that represents the event. We can use event.currentTarget to get the function for the proxy event;

Complete function:

function eventDelegate (parentSelector, targetSelector, events, Foo) {/ / function to be executed by the trigger function triFunction (e) {/ / compatibility with var event = e | | window. The event; / / to get to the target stage to elements of the var target = event. The target | | event. The srcElement; CurrentTarget = event.currenttarget; // Handle matches compatibility if (! Element.prototype.matches) { Element.prototype.matches = Element.prototype.matchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector || Element.prototype.webkitMatchesSelector || function(s) { var matches = (this.document || this.ownerDocument).querySelectorAll(s), i = matches.length; while (--i >= 0 && matches.item(i) ! == this) {} return i > -1; }; } // Traverse the outer layer and match while (target! Matches (target. Matches (targetSelector)) {var sTarget = target; / / perform binding function, pay attention to this foo call (sTarget, Array. Prototype. Slice. The call (the arguments)} target = target. ParentNode; Events.split ('.').foreach (function (evt) {// Multiple parent elements need to be individually bound Array.prototype.slice.call(document.querySelectorAll(parentSelector)).forEach(function ($p) { $p.addEventListener(evt, triFunction); }); }); }Copy the code

Using functions:

eventDelegate('#list', 'li', 'click', function () { console.log(this); });

Copy the code

When clicked, you can see that the # List Li element object appears on the console;

limitations

Of course, event delegation also has some limitations;

For example, events such as Focus and blur have no event bubble mechanism, so they cannot be delegated.

Although events such as Mousemove and Mouseout have bubbled, they can only be calculated and positioned by location, which is high in performance and therefore not suitable for event delegation.

The article has wrong place welcome to correct!