In the last article “JS Knowledge Hodgepodge” mentioned the event flow but did not introduce it in detail, this article will introduce the event flow.

The event flow consists of three phases:

1. Capture stage 2. Target stage 3Copy the code

There are DOM0 levels (on+type) and DOM2 levels (addEventListener) for event binding. I’m going to use the id to get the element for convenience.

  • DOM0 level
<div id="box1"></div>
box1.onclick = function(){
	console.log('box1');
}
Copy the code

It prints box1, which we all know, so let’s see.

<div id="box1"></div>
box1.onclick = function(){
	console.log('box1');
}
box1.onclick = function(){
	console.log('box1 two');
}
Copy the code

Box1 two is printed because the DOM0 level overrides previous bindings on the same element, so let’s see.

<div id="box1">
	<div id="box2">
		<div id="box3"></div>
	</div>
</div>
box1.onclick = function(){
	console.log('box1');
}
box2.onclick = function(){
	console.log('box2');
}
box3.onclick = function(){
	console.log('box3');
}
Copy the code

When we click on box1, we know that it prints box1, but what pops up when we click on box3?

You may be wondering, why does my click on Box3 trigger other boxes? becauseThe event bubbling. So what is event bubbling? Concept please baidu, directly above.

And that’s called the event bubbling up and up and up until I don’t draw the window here. DOM0 level only supports the bubbling phase.

  • DOM2 level
<div id="box1">
	<div id="box2">
		<div id="box3"></div>
	</div>
</div>
box1.addEventListener('click', function(){
	console.log('box1');
},false);
box2.addEventListener('click', function(){
	console.log('box2');
},false);
box3.addEventListener('click', function(){
	console.log('box3');
},false);
Copy the code

The output is the same as above because we are bound to the bubble phase. (True capture, false bubble). Now what about the capture phase

<div id="box1">
	<div id="box2">
		<div id="box3"></div>
	</div>
</div>
box1.addEventListener('click', function(){
	console.log('box1');
},true);
box2.addEventListener('click', function(){
	console.log('box2');
},true);
box3.addEventListener('click', function(){
	console.log('box3');
},true);
Copy the code

Let’s click on Box3 and seeYou might notice that the order is reversed, but why is that? becauseEvent captureWhat is time capture? Concept please baidu, directly above.

This is the capture phase, as opposed to the bubble phase.

What is the order of bubbling and capturing? I’ve tied the same event for both phases to each element, and let’s look at the order in which it fires.

<div id="box1"> <div id="box2"> <div id="box3"></div> </div> </div> box1.addEventListener('click', Function (){console.log('box1 capture phase '); },true); Box2.addeventlistener ('click', function(){console.log('box2 capture phase '); },true); Box3. addEventListener('click', function(){console.log('box3 capture phase '); },true); Box1.addeventlistener ('click', function(){console.log('box1 bubble stage '); },false); Box2. addEventListener('click', function(){console.log('box2 bubble stage '); },false); Box3. addEventListener('click', function(){console.log('box3 bubble stage '); },false);Copy the code

We click on Box3 and see that the capture is followed by bubbling.Is that always the case? Let’s change it a little bit.

Box1.addeventlistener ('click', function(){console.log('box1 capture phase '); },true); Box2.addeventlistener ('click', function(){console.log('box2 capture phase '); },true); Box1.addeventlistener ('click', function(){console.log('box1 bubble stage '); },false); Box2. addEventListener('click', function(){console.log('box2 bubble stage '); },false); Box3. addEventListener('click', function(){console.log('box3 bubble stage '); },false); Box3. addEventListener('click', function(){console.log('box3 capture phase '); },true); // Place the capture phase of Box3 after the bubble phase of Box3Copy the code

See if the sequence is still the same?

It turns out the other way around, this is actually calledThe target stage. Bubble capture is undifferentiated on the target element that you fire the event on, in the order of the binding.

Let’s look at it graphically.

Isn’t that too easy let’s do something more complicated.

<div id="box1"> <div id="box2"> <div id="box3"></div> </div> </div> box1.addEventListener('click', Function (){console.log('box1 capture phase '); },true); Box2.addeventlistener ('click', function(){console.log('box2 capture phase '); },true); Box3. addEventListener('click', function(){console.log('box3 capture phase '); },true); Box1.addeventlistener ('click', function(){console.log('box1 bubble stage '); },false); Box2. addEventListener('click', function(){console.log('box2 bubble stage '); },false); Box3. addEventListener('click', function(){console.log('box3 bubble stage '); },false); box1.onclick = function () { console.log('box1 51561'); } box2.onclick = function () { console.log('box2'); } box3.onclick = function () { console.log('box3'); } box1.onclick = function () { console.log('box1'); }Copy the code

What is the triggering sequence? (I think you’d better write down the answer yourself first.)

Let’s see if you got it right

Is that too easy? Let’s switch the order

box1.onclick = function () { console.log('box1 51561'); } box2.onclick = function () { console.log('box2'); } box3.onclick = function () { console.log('box3'); } box1.onclick = function () { console.log('box1'); } box1.adDeventListener ('click', function(){console.log('box1 capture stage '); },true); Box2.addeventlistener ('click', function(){console.log('box2 capture phase '); },true); Box1.addeventlistener ('click', function(){console.log('box1 bubble stage '); },false); Box2. addEventListener('click', function(){console.log('box2 bubble stage '); },false); Box3. addEventListener('click', function(){console.log('box3 bubble stage '); },false); Box3. addEventListener('click', function(){console.log('box3 capture phase '); },true);Copy the code

Is that right?

  • Again, bubble and capture
<div id="box1"> <div id="box2"> <div id="box3"></div> </div> </div> box1.onclick = function () { console.log('box1 51561 '); } box2.onclick = function () { console.log('box2'); } box3.onclick = function () { console.log('box3'); } box1.onclick = function () { console.log('box1'); } box1.adDeventListener ('click', function(){console.log('box1 capture stage '); },true); Box2.addeventlistener ('click', function(){console.log('box2 capture phase '); },true); Box1.addeventlistener ('click', function(){console.log('box1 bubble stage '); },false); Box2. addEventListener('click', function(){console.log('box2 bubble stage '); },false); Box3. addEventListener('click', function(){console.log('box3 bubble stage '); },false); Box3. addEventListener('click', function(){console.log('box3 capture phase '); },true);Copy the code

What do you think the output will be? Did you hesitate? Which means you still don’t understand bubbling and capture.

Don’t let what you see fool you, bubbling is a parent-child relationship to the DOM structure rather than a seemingly wrapped relationship. (Same answer as above).

  • Forget IE, let’s talk about the event mechanism of IE.

IE does not support addEventListener but it does have attachEvent

box1.onclick = function () {
	console.log('box1 51561');
}
box2.onclick = function () {
	console.log('box2');
}
box3.onclick = function () {
	console.log('box3');
}
box1.onclick = function () {
	console.log('box1');
}
box1.attachEvent('onclick', function (){
	console.log('box1 attachEvent')
})
box2.attachEvent('onclick', function (){
	console.log('box2 attachEvent')
})
box3.attachEvent('onclick', function (){
	console.log('box3 attachEvent')
})
Copy the code

box1.attachEvent('onclick', function (){
	console.log('box1')
})
box1.attachEvent('onclick', function (){
	console.log('box2')
})
box1.attachEvent('onclick', function (){
	console.log('box3')
})
Copy the code

What does this output? (Hint: it won’t be overwritten.) The answer is: Box1 box2 box3 haha, you’re kidding.

Isn’t it strange that in IE this event is bound first and then output. IE6, 7, and 8 do not support event capture, only event bubbling.

  • Prevent event bubbling compatibility

Let’s start with events that don’t support bubbling: Blur, focus, MouseEnter, mouseleave. (That’s all I know) Again, let’s look at preventing bubbling.

box1.onclick = function (){
	console.log('box1')
}
box2.onclick = function (){
	console.log('box2')
}
box3.onclick = function (e){
	e.stopPropagation();
	console.log('box3')
}
Copy the code

Only box3 is output. This prevents bubbling but does not work in IE8 and below, so let’s look at the compatible notation.

function stopPropagate(e){ var event = e || window.event; if(event.stopPropagation){ event.stopPropagation(); }else if(event.cancelBubble){ //IE event.cancelBubble = true; }}Copy the code

Prevents default event compatibility

function preventDef(e){
	var g = e || window.event;
	if(g.preventDefault){
		g.preventDefault();
	}else if(g.returnValue){
		g.returnValue = false;
	}
	return false;
}
Copy the code

Let’s look again at the this pointer to the event binding

box1.onclick = function (){
	console.log('onclick', this);
}
box1.addEventListener('click',function () {
	console.log('addEventListener', this);
}, false)
Copy the code

In IE6, 7, 8, the event bound this pointer

box1.attachEvent('onclick',function () {
	console.log('attachEvent', this);
})
Copy the code

attachEvent [object Window]

We find that IE6, ie7, ie8 this points to the window

  • Extended event delegate
<ul id="ul">
	<li>1</li>
	<li>2</li>
	<li>3</li>
</ul>
Copy the code

If we had to listen in on every Li, would you do it?

let li = document.getElementsByTagName("li"); for (var i = 0; i < li.length; i++) { li.onclick = ()=>{ console.log(i); }}Copy the code

One of the bigwigs in the comments section pointed out my mistake because it created a closure and didn’t get output. I would like to apologize to the readers for not noticing this situation because I was just trying to demonstrate it. The following is after the change, the reason why I do not modify the mistake is to warn myself and others who make the same mistake as me.

let li = document.getElementsByTagName("li"); //ES6 for (let i = 0; i < li.length; i++) { li[i].onclick = ()=>{ console.log(i); }} //IIFE (1) for (var I = 0; i < li.length; i++) { (function(j){ li[j].onclick = ()=>{ console.log(j); }})(I)} for (var I = 0; i < li.length; i++) { li[i].onclick = (function(j){ return ()=>{ console.log(j); } })(i); }Copy the code

If it is right to single is not good enough, plus a few li or have a lot of 100 | 1000 li is done so you still feel not good. We need to register events for every LI. It is not good to register many events. So what we can do, using event delegate, is to delegate your events to someone else (parent), use event bubble, specify only one event handler, and manage all events of a certain type.

Let’s see.

ul.onclick = function (e){
	console.log(e.target);
}
Copy the code

This is the event delegate.

  • Advantages:
    • It can greatly save memory footprint and reduce event registration.
    • When a child object is added, it does not need to be event bound, especially for dynamic content
  • Disadvantages:
    • Common use of event brokers should be limited to the above requirements; if you use event brokers for all events, event misjudgment may occur. That is, events are bound to events that should not be raised.

Fixed:

In Chrome 89.0.4363.0 and later, target elements no longer trigger in the order they were registered! Instead, it captures and then bubbles!