If you understand this article, you will no longer be confused about event binding and event bubbling in javascript

1, Several ways of event binding

Generally speaking, there are two ways for javascript to bind event handlers to DOM: binding in html documents and binding in js code. The following methods 1 and 2 belong to binding events in html, and methods 3, 4 and 5 belong to binding events in js code. Method 5 is the most recommended method.

Method 1: the DOM element of HTML supports onclick, onblur and other attributes starting with on. We can directly write javascript code in these attribute values. When you click div, the following code will pop up the ID of div:

<div id="outestA" onclick="var id = this.id;alert(id);return false;"></div> 

This approach is obviously not good, because the code is placed in the string and cannot be formatted and typeset. When there are a lot of code, it is difficult to understand. It's worth noting here: this in the onclick attribute represents the DOM object currently clicked, so we can use this Gets the value of the DOM id attribute of the element.

Method 2: when there are many codes, we can specify the function name in onclick and other attributes.

<script> 
  function buttonHandler(thisDom) 
  { 
    alert(this.id);//undefined 
    alert(thisDom.id);//outestA 
    return false; 
  } 
</script> 
<div id="outestA" onclick="return buttonHandler(this);"></div> 

Compared with the above method, this method is slightly better. It is worth mentioning that this in the event handler function represents the window object, so we pass the dom object as a parameter through this in the onclick attribute value.

Method 3: use onclick and other attributes of dom element in JS code

var dom = document.getElementById("outestA"); 
dom.onclick = function(){alert("1=" + this.id);}; 
dom.onclick = function(){alert("2=" + this.id);}; 

In this way, this represents the current DOM object. Another point: this method can only bind one event handler, and the latter will overwrite the former.

Method 4: use attachEvent/detachEvent function to bind and cancel events under IE.

attachEvent/detachEvent has poor compatibility. IE6~IE11 support this function, but FF and Chrome browsers do not support this method. Moreover, attachEvent/detachEvent is not a W3C standard practice, so it is not recommended. In IE browser, attachEvent has the following features.

a) In the event handler function, this represents a window object, not a dom object.

var dom = document.getElementById("outestA"); 
dom.attachEvent('onclick',a); 

function a() 
{ 
  alert(this.id);//undefined 
}

b) The same event handler can only be bound once.

var dom = document.getElementById("outestA"); 
dom.attachEvent('onclick',a); 
dom.attachEvent('onclick',a); 
function a() 
{ 
    alert(this.id); 
} 

Although attachEvent is bound twice, function a will only be called once.

c) Different function objects can be bound repeatedly without overwriting.

var dom = document.getElementById("outestA"); 
dom.attachEvent('onclick',function(){alert(1);}); 
dom.attachEvent('onclick',function(){alert(1);}); 
// When the click event of outestA occurs, two dialog boxes will pop up

Anonymous functions and anonymous functions are different from each other, even if the code is exactly the same. Therefore, if we want to use detachEvent to cancel the event handling function of attachEvent binding, anonymous functions cannot be used when binding events. The event handling function must be written as a separate function, otherwise it cannot be cancelled.

Method 5: use the W3C standard addEventListener and removeEventListener. These two functions are specified in W3C standard, supported by FF and Chrome browsers, and not supported by IE6/IE7/IE8. However, these two standard API s have been supported since IE9.

// type:Event type,Excluding"on",such as"click","mouseover","keydown"; 
// and attachEvent Event name,Containing"on",such as"onclick","onmouseover","onkeydown"; 
// listener:Event handler 
// useCapture It's an event bubble,Or event capture,default false,Represents the event bubble type 
addEventListener(type, listener, useCapture);

a) In the event handler function, this represents a dom object, not a window. This feature is different from attachEvent.

var dom = document.getElementById("outestA"); 
dom.addEventListener('click', a, false); 

function a() 
{ 
    alert(this.id);//outestA 
}

b) The same event handler function can be bound twice, one for event capture and one for event bubbling.

var dom = document.getElementById("outestA"); 
dom.addEventListener('click', a, false); 
dom.addEventListener('click', a, true); 

function a() 
{ 
  alert(this.id);//outestA 
} 

// When clicked outestA When,function a It will be called twice

If the same event handler function is bound and all of them are event bubble type or event capture type, you can only bind it once.

var dom = document.getElementById("outestA"); 
dom.addEventListener('click', a, false); 
dom.addEventListener('click', a, false); 

function a() 
{ 
  alert(this.id);//outestA 
} 

// When clicked outestA When,function a It will only be called once

 

c) Different event handling functions can be bound repeatedly, which is consistent with attachEvent.

2, Execution order of event handler functions

Mode 1, mode 2 and mode 3 cannot realize the repeated binding of events, so there is naturally no problem of execution order. Mode 4 and mode 5 can repeat the binding feature, so you need to understand the next execution order. If you write code that depends on the order of execution, you can conclude that there is a problem with your design. Therefore, the following sequence questions are only discussed as interest, which has no practical significance. Direct conclusion: the performances of addEventListener and attachEvent are consistent. If multiple processing functions are bound to the same event, those bound first shall be executed first. I have tested the following code in IE11, FF17 and chrome 39.

<script> 
window.onload = function(){ 
  var outA = document.getElementById("outA"); 
  outA.addEventListener('click',function(){alert(1);},false); 
  outA.addEventListener('click',function(){alert(2);},true); 
  outA.addEventListener('click',function(){alert(3);},true); 
  outA.addEventListener('click',function(){alert(4);},true); 
}; 
</script> 

<body> 
<div id="outA" style="width:400px; height:400px; background:#CDC9C9;position:relative;"> 
</div> 
</body> 

 

When you click outA, 1, 2, 3 and 4 will be printed out in turn. Special attention should be paid here: we bind multiple onclick event handling functions to outA, which is also the event triggered by directly clicking outA. Therefore, the problem of event bubbling and event capture is not involved, that is, the third parameter of addEventListener is useless in this scenario. If the click event of outA is triggered by event bubbling or event capture, the execution order of the function will change.


3, Event bubbling and event capture

Event bubbling and event capture are well understood. They are just different views on the same thing, but both of them are very reasonable. We know that elements in HTML can be nested to form a hierarchical relationship similar to a tree. For example, the following code:

<div id="outA" style="width:400px; height:400px; background:#CDC9C9;position:relative;"> 
  <div id="outB" style="height:200; background:#0000ff;top:100px;position:relative;"> 
    <div id="outC" style="height:100px; background:#FFB90F;top:50px;position:relative;"></div> 
  </div> 
</div>

 

If the innermost outC is clicked, are the outermost outB and outC clicked? Obviously, otherwise, there is no need to distinguish between event bubbling and event capture. There is no doubt about this among browser manufacturers. If outA, outB and outC all register click type event handling functions, when you click outC, is the trigger order a -- > B -- > C or C -- > B -- > A? If the browser adopts event bubbling, the trigger sequence is C -- > B -- > A, which floats from the bottom to the surface like bubbles from the inside out; If event capture is used, the trigger sequence is a -- > B -- > C, from top to bottom, falling from the surface to the bottom like a stone. See the following figure for event bubbling:

 

 

See the following figure for event capture:

 

 

Generally speaking, IE8 only supports some events before bubbling. Both IE9+/FF/Chrome models are supported and can be set through useCapture of addEventListener((type, listener, useCapture). useCapture=false represents event bubbling, and useCapture=true represents event capture.

<script> 
window.onload = function(){ 
  var outA = document.getElementById("outA"); 
  var outB = document.getElementById("outB"); 
  var outC = document.getElementById("outC"); 

  // Use event bubbling 
  outA.addEventListener('click',function(){alert(1);},false); 
  outB.addEventListener('click',function(){alert(2);},false); 
  outC.addEventListener('click',function(){alert(3);},false); 
}; 
</script> 

<body> 
  <div id="outA" style="width:400px; height:400px; background:#CDC9C9;position:relative;"> 
    <div id="outB" style="height:200; background:#0000ff;top:100px;position:relative;"> 
      <div id="outC" style="height:100px; background:#FFB90F;top:50px;position:relative;"></div> 
    </div> 
  </div> 
</body> 

 

Event bubbling is used. When you click outC, the printing order is 3 -- > 2 -- > 1. If you change false to true and use event capture, the printing order is 1 -- > 2 -- > 3.


4, DOM event flow

I don't know how to explain DOM event flow. Personally, I feel that it is a combination of event bubbling and event capture. Just look at the picture.

 

 

DOM event flow: the event is divided into three stages: capture stage, target stage and bubble stage. Call the handler of the capture phase first, then the handler of the target phase, and finally the handler of the bubble phase. This process is very similar to the action and Interceptor in the struts 2 box. When a URL request is issued, the pre Interceptor is called first, then the action is called, and finally the post Interceptor is called.

<script> 
window.onload = function(){ 
  var outA = document.getElementById("outA"); 
  var outB = document.getElementById("outB"); 
  var outC = document.getElementById("outC"); 

  // target(Self triggering event,It doesn't matter whether it's bubbling or trapping) 
  outC.addEventListener('click',function(){alert("target");},true); 

  // Event Bubbling  
  outA.addEventListener('click',function(){alert("bubble1");},false); 
  outB.addEventListener('click',function(){alert("bubble2");},false); 

  // Event capture 
  outA.addEventListener('click',function(){alert("capture1");},true); 
  outB.addEventListener('click',function(){alert("capture2");},true); 
}; 
</script> 

<body> 
  <div id="outA" style="width:400px; height:400px; background:#CDC9C9;position:relative;"> 
    <div id="outB" style="height:200; background:#0000ff;top:100px;position:relative;"> 
      <div id="outC" style="height:100px; background:#FFB90F;top:50px;position:relative;"></div> 
    </div> 
  </div> 
</body> 

 

When you click outC, capture1 -- > Capture2 -- > target -- > bubble2 -- > bubble1 will be printed out in turn. Can you understand the meaning of the third parameter useCapture in the API of addEventListener(type,handler,useCapture)? useCapture=false means: add the event handler function to the bubbling stage, and it will be called in the bubbling stage; useCapture=true means that the event handler function is added to the capture phase and will be called in the capture phase. From the DOM event flow model, it can be seen that the event handling function in the capture stage must be executed before the event handling function in the bubble stage.


5, On the execution sequence of event functions

Mentioned in DOM event flow:

// target(Self triggering event,It doesn't matter whether it's bubbling or trapping) 
outC.addEventListener('click',function(){alert("target");},true); 

 

We trigger the onclick event on outC (this is the target object). What happens if we bind the capture phase / bubble phase event handler on outC at the same time?

<script> 
window.onload = function(){ 
  var outA = document.getElementById("outA"); 
  var outB = document.getElementById("outB"); 
  var outC = document.getElementById("outC"); 

  // target(Self triggering event,It doesn't matter whether it's bubbling or trapping) 
  outC.addEventListener('click',function(){alert("target2");},true); 
  outC.addEventListener('click',function(){alert("target1");},true); 

  // Event Bubbling  
  outA.addEventListener('click',function(){alert("bubble1");},false); 
  outB.addEventListener('click',function(){alert("bubble2");},false); 

  // Event capture 
  outA.addEventListener('click',function(){alert("capture1");},true); 
  outB.addEventListener('click',function(){alert("capture2");},true); 
}; 
</script> 

<body> 
  <div id="outA" style="width:400px; height:400px; background:#CDC9C9;position:relative;"> 
    <div id="outB" style="height:200; background:#0000ff;top:100px;position:relative;"> 
      <div id="outC" style="height:100px; background:#FFB90F;top:50px;position:relative;"></div> 
    </div> 
  </div> 
</body> 

 

When you click outC, the printing order is: capture1 -- > Capture2 -- > target2 -- > Target1 -- > bubble2 -- > bubble1. Since outC is the target object for triggering events, the event handler registered on outC belongs to the target stage in DOM event flow. Execution order of target stage functions: those registered first will be executed first, and those registered later will be executed later. This is what we said above. It doesn't matter whether the function bound on the target object adopts capture or bubble, because bubble and capture only affect the execution order of the function on the parent element, and have no impact on themselves. If you don't believe it, you can put the following code in for verification.

// target(Self triggering event,It doesn't matter whether it's bubbling or trapping) 
outC.addEventListener('click',function(){alert("target1");},false); 
outC.addEventListener('click',function(){alert("target2");},true); 
outC.addEventListener('click',function(){alert("target3");},true); 
outC.addEventListener('click',function(){alert("target4");},false);

 

So far, we can conclude the execution order of event functions: the processing functions in the capture stage are executed first, followed by the processing functions in the target stage, and finally the processing functions in the bubbling stage. The processing function of the target stage, which is registered first and then executed.

 

6, Prevent event bubbling and capture

By default, multiple event handlers are executed in the order in the DOM event flow model. If an event occurs on the child element and there is no need to execute the event handler registered on the parent element, we can stop capturing and bubbling and avoid meaningless function calls. The five event binding methods mentioned above can prevent the propagation of events. Because the fifth way is the most recommended practice. So based on the fifth way, let's see how to prevent the spread of events. IE8 and before can be through window event. Cancelbubble = true to prevent the continuous propagation of the event; IE9+/FF/Chrome via event Stoppropagation() prevents the event from continuing to propagate.

<script> 
window.onload = function(){ 
  var outA = document.getElementById("outA"); 
  var outB = document.getElementById("outB"); 
  var outC = document.getElementById("outC"); 

  // target 
  outC.addEventListener('click',function(event){ 
    alert("target"); 
    event.stopPropagation(); 
  },false); 

  // Event Bubbling  
  outA.addEventListener('click',function(){alert("bubble");},false); 

  // Event capture 
  outA.addEventListener('click',function(){alert("capture");},true); 

}; 

</script> 

<body> 
  <div id="outA" style="width:400px; height:400px; background:#CDC9C9;position:relative;"> 
    <div id="outB" style="height:200; background:#0000ff;top:100px;position:relative;"> 
      <div id="outC" style="height:100px; background:#FFB90F;top:50px;position:relative;"></div> 
    </div> 
  </div> 
</body> 

 

When you click outC, capture -- > target will be printed instead of bubble. When the event is propagated to the handler on outC, the event is prevented from continuing to propagate through stopPropagation, so it will not continue to propagate to the bubbling stage.

Finally, let's look at a more interesting piece of code:

<script> 

window.onload = function(){ 
  var outA = document.getElementById("outA"); 
  var outB = document.getElementById("outB"); 
  var outC = document.getElementById("outC"); 

  // target 
  outC.addEventListener('click',function(event){alert("target");},false); 

  // Event Bubbling  
  outA.addEventListener('click',function(){alert("bubble");},false); 

  // Event capture 
  outA.addEventListener('click',function(){alert("capture");event.stopPropagation();},true); 
}; 
</script> 

<body> 
  <div id="outA" style="width:400px; height:400px; background:#CDC9C9;position:relative;"> 
    <div id="outB" style="height:200; background:#0000ff;top:100px;position:relative;"> 
      <div id="outC" style="height:100px; background:#FFB90F;top:50px;position:relative;"></div> 
    </div> 
  </div> 
</body>

 

As a result, only capture will be printed, and target and bubble will not be printed. Miraculously, we clicked outC, but instead of triggering the event handler on outC, we triggered the event handler on outA. The reason is not explained. If you don't understand it, you can read this article again.

 

 

Posted by jcleary on Sat, 14 May 2022 18:55:04 +0300