【我眼中的】 - 【13】事件代理及其应用场景

1.什么是“事件代理”?

事件代理(Event Delegation)也称之为事件委托。是JS中常用的绑定事件的技巧。
事件代理是把原来需要绑定在子元素的响应事件委托给父元素,让父元素担当事件监听的职务。通俗来讲就是把一个元素的响应的事件(clickkeydown等)的函数委托到其父元素上。

事件代理的原理是 DOM元素的事件冒泡

一个事件触发后,会在子元素和父元素之间传播。这种传播分为三个阶段:

  • 捕获阶段:从window对象传导到目标节点(从上层传到底层)称为“捕获阶段”,在这一阶段不会响应任何事件。
  • 目标阶段:在目标节点上触发,称为“目标阶段”。
  • 冒泡阶段:从目标节点传导回window对象(从底层传回上层),称为“冒泡阶段”。事件代理即是利用事件冒泡的机制把里层需要相应的事件绑定到外层。

事件流的都会经过三个阶段: 捕获阶段 -> 目标阶段 -> 冒泡阶段,而事件委托就是在冒泡阶段完成

2.应用场景

如果有一个列表,列表之中有大量的列表项,我们需要在点击列表项的时候响应一个事件

1
2
3
4
5
6
7
<ul id="list">
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
......
<li>item n</li>
</ul>

如果给每一个列表项一一都绑定一个函数,对于内存消耗是非常大的

1
2
3
4
5
6
7
8
// 获取目标元素
const lists = document.getElementsByTagName('li');
// 循环遍历绑定事件
for(let i=0;i<lists.length;i++){
lists[i].onclick = function(e){
console.log(e,target.innerHTML);
}
}

这个时候可以采用事件委托,把点击事件绑定在父元素ul上,然后执行时间的时候再去匹配目标元素

1
2
3
4
5
6
7
8
9
10
// 给父层元素绑定事件
document.getElementById('list').addEventListener('click',function(e){
//兼容性处理
var event = e || window.event;
var target = event.target || event.srcElement;
// 判断是否匹配目标元素
if(target.nodeName.toLocaleLowerCase === 'li'){
console.log('the content is:',target.innerHTML);
}
})

还有一种场景是 上述列表项并不多,我们给每个列表项都绑定了事件。但是如果用户能够随时动态的增加或者去除列表项元素,那么在每一次改变的时候都需要重新给新增元素绑定事件,给即将删去的元素解绑事件。
如果采用事件委托就没有这种麻烦。因为事件是绑定在父层元素的,和目标元素的增减是没有关系的,执行到目标元素是在真正响应执行事件函数的过程中去匹配的

示例:

下面html结构中,点击input可以动态添加元素

1
2
3
4
5
6
7
<input type="button" name="" id="btn" value="添加" />
<ul id="ul1">
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
<li>item 4</li>
</ul>

使用事件委拖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const oBtn = document.getElementById('btn');
const oUl = document.getElementById('ul1');
const num = 4;

// 事件委托,添加的子元素也有事件
oUl.onclick = function(e){
e = e || window.event;
const target = e.target || e.srcElement;
if(target.nodeName.toLocaleLowerCase === 'li'){
console.log('the content is:',target.innerHTML);
}
};

// 添加新节点
oBtn.onclick = function(){
num++;
const oLi = document.createElement('li');
oLi.innerHTML = `item ${num}`;
oUl.appendChild(oLi);
}

可以看到,利用事件委托在动态绑定事件的情况下是可以减少很多重复工作的。

3.总结

适合事件委托的事件有:clickmousedownkeydownkeyupkeypress
事件委托有两大优点:

  • 减少整个页面所需要的内存,提升整体性能
  • 动态绑定,减少重复工作

事件委托也存在局限性:

  • focusblur这些事件没有事件冒泡机制,所以无法委托绑定事件
  • mousemovemouseout这样的事件,虽然有事件冒泡,但是只能通过位置去计算定位,对性能消耗高,也不适合事件委托
  • 如果把所有事件都用事件代理,可能会出现事件误判,即本不该被触发的事件被绑定上了事件

事件委托中有两个对象targetcurrentTarget,两者的含义不同:

  • target:事件触发的真实元素(点击)
  • currentTarget:事件绑定元素(绑定)
  • 事件处理函数中的this指向为currentTarget

文章作者: qinwei
文章链接: https://qw-null.github.io/2022/08/09/13-我眼中的-事件代理及其应用场景/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 QW's Blog