JavaScript事件流和事件委托
1.事件传播机制(V模型)
想要深入了解委托,最好先理解事件传播机制:
我们把事件分为三个阶段:捕获阶段、目标阶段、冒泡阶段;
1 捕获阶段(捕获你要操作的目标元素):
当点击时,先经过捕获阶段,从最外层如html(这里不考虑兼容了)层层找到目标。
2 目标阶段:
经过目标阶段,响应事件。
3 冒泡阶段:
从点击目标往外层层触发相同的事件方法直到最外层(根节点)。
2.关于事件绑定
事件绑定,有
DOM0级(on+type)和DOM2级(addEventListener)
准备一个盒子
<div id='box'>
</div>
dom0:在属性上挂载,同一个元素只能有一个点击事件,多个点击事件,后者会覆盖前者;
const divEl = document.querySelector("#box");
divEl.onclick = function(){
console.log(1);
}
divEl.onclick = function(){
console.log(2);
}
【点击这个元素打印的是2,前一个被后一个覆盖了】
dom2:在EventTarget.prototype定义的,同一个元素多个点击事件不会覆盖,都会执行;
【原理是有一个统一的事件池;触发时,浏览器会把事件池中所有的按照存放顺序发放】
const divEl = document.querySelector("#box");
divEl.addEventListener("click",function(e){
console.log(1)
})
divEl.addEventListener("click",function(e){
console.log(2)
})
【点击这个box元素,1,2都会被打印的】
3.写一个事件冒泡
使用DOM0级事件绑冒泡
<div id='box1'>1
<div id='box2'>2
<div id='box3'>3</div>
</div>
</div>
const divEl1 = document.querySelector("#box1");
const divEl2= document.querySelector("#box2");
const divEl3= document.querySelector("#box3");
divEl1.onclick = function(){
console.log(1);
}
divEl2.onclick = function(){
console.log(2);
}
divEl3.onclick = function(){
console.log(3);
}
当我们点击最内层的3号盒子,你发现绑定在外面的盒子的方法也被调用了,这个就是这个事件冒泡执行了
使用DOM1级事件绑冒泡
divEl1.addEventListener("click",function(e){
console.log(1)
},false)
divEl2.addEventListener("click",function(e){
console.log(2)
},false)
divEl3.addEventListener("click",function(e){
console.log(3)
},false)
输出跟上面是一样的,因为我们绑定在了冒泡阶段。(true捕获,false冒泡)
4.写一个事件捕获
【因为`Dom0`级事件,只支持事件冒泡,所以只能用`DOM1`级事件绑定写一个事件捕获】
divEl1.addEventListener("click",function(e){
console.log(1)
},true)
divEl2.addEventListener("click",function(e){
console.log(2)
},true)
divEl3.addEventListener("click",function(e){
console.log(3)
},true)
同样点击最里面的盒子
你会发现,先执行的最外层盒子,很容易理解啊,因为捕获是从外层开始找目标元素的。
5.验证捕获和冒泡的执行顺序
我们给上面的元素,使用DOM2级事件绑定同时绑定冒泡和捕获事件
const divEl1 = document.querySelector("#box1");
const divEl2= document.querySelector("#box2");
const divEl3= document.querySelector("#box3");
divEl1.addEventListener("click",function(e){
console.log(1,"捕获阶段")
},true)
divEl2.addEventListener("click",function(e){
console.log(2,"捕获阶段")
},true)
divEl3.addEventListener("click",function(e){
console.log(3,"捕获阶段")
},true)
divEl1.addEventListener("click",function(e){
console.log(1,"冒泡阶段")
},false)
divEl2.addEventListener("click",function(e){
console.log(2,"冒泡阶段")
},false)
divEl3.addEventListener("click",function(e){
console.log(3,"冒泡阶段")
},false)
点击内部的3号盒子,符合我们的预期,先执行捕获这个元素,然后执行目标元素的方法,然后冒泡出去!
6.目标元素的冒泡和捕获顺序
上面我们点击的都是最内部的3号元素,也是我们的目标元素,也是我们
V模型的转折点,这里的冒泡和捕获感觉可以以任何顺序执行,我们来验证一下!我们换一下执行顺序看看
divEl1.addEventListener("click",function(e){
console.log(1,"捕获阶段")
},true)
divEl2.addEventListener("click",function(e){
console.log(2,"捕获阶段")
},true)
//这里我们把目标元素的冒泡阶段写在前面,这样js执行主线程会先执行这里
divEl3.addEventListener("click",function(e){
console.log(3,"冒泡阶段")
},false)
divEl3.addEventListener("click",function(e){
console.log(3,"捕获阶段")
},true)
//==========================================================
divEl1.addEventListener("click",function(e){
console.log(1,"冒泡阶段")
},false)
divEl2.addEventListener("click",function(e){
console.log(2,"冒泡阶段")
},false)
我们看一下执行的结果把
可以看出来!目标元素的冒泡捕获,和代码执行的顺序有关系的
7.关于事件委托
【那什么叫事件委托呢?】
顾名思义!就是把事件委托给别人执行,那么在dom树中,我们只能理解为,把应该在自身上面触发的事件,委托给别的dom元素,那么怎么做到的呢?就是使用事件冒泡。
【事件委托的常用场景】
因为在页面中,绑定事件越多,浏览器内存占用越大,严重影响性能 。比如我们100条数据,循环出来100个li标签去渲染,然后对每个li标签还要添加一些操作事件,这样的话占用内存很大,
<div id='box1'>
</div>
const divEl1 = document.querySelector("#box1");
const data = 100;
for(let i = 0;i < data;i++){
const li = document.createElement("li");
li.innerHTML = i;
li.addEventListener("click",function(){
console.log(li.innerHTML);
})
divEl1.appendChild(li)
}
所以我们一般使用事件委托,利用冒泡机制,委托给他的父级元素。
const divEl1 = document.querySelector("#box1");
const data = 100;
for (let i = 0; i < data; i++) {
const li = document.createElement("li");
li.innerHTML = i;
divEl1.appendChild(li)
}
//绑定给他的父元素
divEl1.addEventListener("click", function (e) {
console.log(e.target.innerHTML);
})
一样可以完成需求!
8.委托的局限性
1 比如 focus、blur 之类的事件本身没有事件冒泡机制,所以无法委托;
2 mousemove、mouseout这样的事件,虽然有事件冒泡,但是只能不断通过位置去计算定位,
9.使用委托的注意项(可以叫应用项)
1 只在必须的地方,使用事件委托,比如:ajax的局部刷新区域
2 尽量的减少绑定的层级,并且不在body元素上,进行绑定;(事件委托的原理离不开DOM的
3 减少绑定的次数,如果可以,那么把多个事件的绑定,合并到一次事件委托中去,由这个事件委托的回调,来进行分发。
writeBy:村望 部分资源来自互联网