Detailed explanation of event delegation or event agent in js

Posted by kapishi on Thu, 03 Mar 2022 11:34:04 +0100

1. Overview:

(1) What is event entrustment?

It also has a name called event agent
In terms of JavaScript Advanced Programming: event delegation is to use event bubbling and specify only one event handler to manage all events of a certain type. What does that mean?
The leaders on the Internet basically use the same example of event entrustment, that is, taking express delivery to explain this phenomenon. It's really appropriate to carefully figure out this example. I'll borrow flowers to offer Buddha here and pick it up. Let's seriously understand the principle of event entrustment:

List of cases:
Three colleagues are expected to receive the express on Monday. There are two ways to sign for express delivery: first, three people wait for express delivery at the door of the company; Second, entrust the front desk MM to sign for it. In reality, we mostly adopt the entrusted scheme (the company will not tolerate so many employees standing at the door to wait for express delivery). After receiving the express, the front desk MM will judge who the recipient is, and then sign for it according to the recipient's requirements, or even pay for it. Another advantage of this scheme is that even if new employees (no matter how many) come to the company, the front desk MM will verify and sign for them after receiving the express sent to the new employees.

There are actually two meanings here:

First, the colleague who entrusts the front desk can sign in on his behalf, that is, the existing dom node in the program has events;

Second, new employees can also be signed in by the foreground MM, that is, the newly added dom node in the program also has events.

2. Why use event delegation:

Generally speaking, dom needs event handlers. We will directly set event handlers for it. What if many DOMS need to add event handlers? For example, we have 100 li, and each li has the same click event. Maybe we will use the for loop method to traverse all li, and then add events to them. What impact will this have?

In JavaScript, the number of event handlers added to the page is directly related to the overall running performance of the page, because it needs to interact with DOM nodes continuously. The more times you visit DOM, the more times the browser redraws and rearranges, which will prolong the interaction readiness time of the whole page, This is why one of the main ideas of performance optimization is to reduce DOM operations; If event delegation is used, all operations will be put into the js program, and the operations with DOM only need to interact once, which can greatly reduce the number of interactions with Dom and improve performance;

Each function is an object, which will occupy memory. The more objects, the greater the memory occupancy rate, and the worse the natural performance (memory is not enough, it is hard injury, ha ha). For example, the above 100 li will occupy 100 memory space. If it is 1000 or 10000, it can only say ha ha. If you use event delegation, Then we can only operate on its parent (if there is only one parent). In this way, we need a memory space. Does it save a lot and the natural performance will be better.

3. Principle of event delegation:

Event delegation is realized by using the bubbling principle of events. What is event bubbling? The event starts from the deepest node and then propagates the event upward step by step. For example, there is a node tree on the page, div > ul > li > A; For example, if you add a click event to the innermost a, the event will be executed outward layer by layer. The execution sequence is a > li > ul > Div. there is such a mechanism. If you add a click event to the outermost div, the inner ul, li and a will bubble on the outermost div when they do click events, so they will be triggered. This is event delegation, Delegate their parents to execute events on their behalf.

4. How to implement event delegation:

Finally, it's the core of this article. Haha, before introducing the method of event delegation, let's take a look at an example of a general method:,

Child nodes achieve the same functions:

<ul id="ul1">
    <li>111</li>
    <li>222</li>
    <li>333</li>
    <li>444</li>
</ul>

The implementation function is to click li and 123 will pop up:

window.onload = function(){
    var oUl = document.getElementById("ul1");
    var aLi = oUl.getElementsByTagName('li');
    for(var i=0;i<aLi.length;i++){
        aLi[i].onclick = function(){
            alert(123);
        }
    }
}

The meaning of the above code is very simple. I believe many people implement it in this way. Let's see how many dom operations there are. First, find the ul, then traverse the li, and then click the li, and then find the location of the target li once to perform the final operation. Each click must find the li;

So what happens if we use event delegation?

window.onload = function(){
    var oUl = document.getElementById("ul1");
   oUl.onclick = function(){
        alert(123);
    }
}

Here, the parent ul is used for event processing. When li is clicked, due to the bubble principle, the event will bubble on the ul. Because there is a click event on the ul, the event will be triggered. Of course, when ul is clicked, it will also be triggered. Then the problem arises. What if I want the effect of event agent to be the same as that of events directly to nodes, For example, it can only be triggered by clicking li. We are not afraid. We have unique skills:

The Event object provides an attribute called target, which can return the target node of the Event. We call it the Event source. In other words, the target can be expressed as the dom of the current Event operation, but it is not the real DOM operation. Of course, this is compatible. The standard browser uses ev Target, IE browser uses Event Srclelement, at this time, only gets the location of the current node and does not know what the node name is. Here, we use nodeName to get the specific tag name. This returns an uppercase, which we need to convert to lowercase for comparison (habit problem):

window.onload = function(){	
     var oUl = document.getElementById("ul1");
     oUl.onclick = function(ev){
     	var ev = ev || window.event;
     	var target = ev.target || ev.srcElement;
     		if(target.nodeName.toLowerCase() == 'li'){
     			alert(123);
     			alert(target.innerHTML);
     		}
     }
}

In this way, only clicking li will trigger events, and dom operations will be executed only once at a time. If there are a large number of li, dom operations will be greatly reduced, and the optimized performance can be imagined!

The above example is to say that li operates with the same effect. If the effect of each li click is different, is it still useful to use event delegation?

<div id="box">
        <input type="button" id="add" value="add to" />
        <input type="button" id="remove" value="delete" />
        <input type="button" id="move" value="move" />
        <input type="button" id="select" value="choice" />
    </div>
 
 window.onload = function(){
      var Add = document.getElementById("add");
      var Remove = document.getElementById("remove");
      var Move = document.getElementById("move");
      var Select = document.getElementById("select");
      
      Add.onclick = function(){
          alert('add to');
      };
      Remove.onclick = function(){
          alert('delete');
      };
      Move.onclick = function(){
          alert('move');
      };
      Select.onclick = function(){
          alert('choice');
      }
      
 }

I won't say much about the effect achieved above. It's very simple. If you click each of the four buttons to do different operations, you need at least four dom operations. If you use event delegation, can you optimize it?

window.onload = function(){
   var oBox = document.getElementById("box");
   oBox.onclick = function (ev) {
       var ev = ev || window.event;
       var target = ev.target || ev.srcElement;
       if(target.nodeName.toLocaleLowerCase() == 'input'){
           switch(target.id){
               case 'add' :
                   alert('add to');
                   break;
               case 'remove' :
                   alert('delete');
                   break;
               case 'move' :
                   alert('move');
                   break;
               case 'select' :
                   alert('choice');
                   break;
           }
       }
   }
   
}

With event delegation, you can complete all the effects with only one dom operation, which is certainly better than the above performance

Now we are talking about the operations under the existing dom node after document loading. If it is a new node, will there be an event on the new node? In other words, when a new employee comes, can he receive the express?

Take a look at the normal method of adding nodes:

<input type="button" name="" id="btn" value="add to" />
<ul id="ul1">
     <li>111</li>
     <li>222</li>
     <li>333</li>
     <li>444</li>
 </ul>

Now move in li, li turns red, move out li, li turns white. Then click the button to add a li child node to the ul

window.onload = function(){
    var oBtn = document.getElementById("btn");
    var oUl = document.getElementById("ul1");
    var aLi = oUl.getElementsByTagName('li');
    var num = 4;
    
    //The mouse turns red when moving in and white when moving out
    for(var i=0; i<aLi.length;i++){
        aLi[i].onmouseover = function(){
            this.style.background = 'red';
        };
        aLi[i].onmouseout = function(){
            this.style.background = '#fff';
        }
    }
    //Add new node
    oBtn.onclick = function(){
        num++;
        var oLi = document.createElement('li');
        oLi.innerHTML = 111*num;
        oUl.appendChild(oLi);
    };
}

This is a common practice, but you will find that there are no events in the newly added li. It means that when adding child nodes, the events are not added together. This is not the result we want. What should we do? The general solution is to wrap the for loop with a function named mhiver, as follows:

window.onload = function(){
  var oBtn = document.getElementById("btn");
  var oUl = document.getElementById("ul1");
  var aLi = oUl.getElementsByTagName('li');
  var num = 4;
  
  function mHover () {
      //The mouse turns red when moving in and white when moving out
      for(var i=0; i<aLi.length;i++){
          aLi[i].onmouseover = function(){
              this.style.background = 'red';
          };
          aLi[i].onmouseout = function(){
              this.style.background = '#fff';
          }
      }
  }
  mHover ();
  //Add new node
  oBtn.onclick = function(){
      num++;
      var oLi = document.createElement('li');
      oLi.innerHTML = 111*num;
      oUl.appendChild(oLi);
      mHover ();
  };
}

Although the function has been realized, it looks good, but in fact, there is no doubt that another dom operation has been added, which is not desirable in terms of optimizing performance. Can we optimize by means of event delegation

window.onload = function(){
    var oBtn = document.getElementById("btn");
    var oUl = document.getElementById("ul1");
    var aLi = oUl.getElementsByTagName('li');
    var num = 4;
    
    //Event delegate, and the added child elements also have events
    oUl.onmouseover = function(ev){
        var ev = ev || window.event;
        var target = ev.target || ev.srcElement;
        if(target.nodeName.toLowerCase() == 'li'){
            target.style.background = "red";
        }
        
    };
    oUl.onmouseout = function(ev){
        var ev = ev || window.event;
        var target = ev.target || ev.srcElement;
        if(target.nodeName.toLowerCase() == 'li'){
            target.style.background = "#fff";
        }
        
    };
    
    //Add new node
    oBtn.onclick = function(){
        num++;
        var oLi = document.createElement('li');
        oLi.innerHTML = 111*num;
        oUl.appendChild(oLi);
    };
}

Look, the above is the way of event delegation. The newly added child elements have event effects. We can find that when using event delegation, we don't need to traverse the child nodes of the element at all. We just need to add events to the parent element. Everything else is executed in js, which can greatly reduce dom operations, This is the essence of event delegation.

Here is a special case scenario:
The question is:

Now, for a scenario ul > li > div > P, div is full of li, P is full of div, or ul binding time, you need to judge whether the clicked is li (assuming that the structure in li is not fixed), then e.target may be p or Div. how would you deal with this situation?

Let's recreate the scene he gave us now

<ul id="test">
        <li>
            <p>11111111111</p>
        </li>
        <li>
            <div>
                22222222
            </div>
        </li>
        <li>
            <span>3333333333</span>
        </li>
        <li>4444444</li>
    </ul>

In the above list, there are four li with different contents. If you click li, the event object must be the currently clicked object. How to assign it to li? I'll give you the solution directly below:

var oUl = document.getElementById('test');
 oUl.addEventListener('click',function(ev){
     var target = ev.target;
     while(target !== oUl ){
         if(target.tagName.toLowerCase() == 'li'){
             console.log('li click~');
             break;
         }
         target = target.parentNode;
     }
 })

The core code is the while loop part, which is actually a recursive call. You can also write a function to call it by recursive method. At the same time, you can use the bubble principle to bubble from inside to outside until you know the currentTarget. When the current target is li, you can execute the corresponding event and then terminate the loop. Well, there's no problem!

There is no effect here. You can copy it and run it!

Summary:

So what kind of events can use event delegation and what kind of events can't?

Events suitable for event delegation: click, mousedown, mouseup, keydown, keyup, keypress.

It is worth noting that although mouseover and mouseout also have event bubbles, they need special attention when dealing with them, because their positions need to be calculated frequently, which is not easy to deal with.

There are many unsuitable ones. For example, mousemove has to calculate its position every time, which is very difficult to control. For example, focus, blur and so on, they don't use the bubbling feature, so naturally they can't use event delegation.

Topics: Javascript Front-end