什么是事件代理
事件代理,可能是代理模式最常见的一种应用方式,也是一道实打实的高频面试题。它的场景是一个父元素下有多个子元素,像这样:
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>事件代理</title></head><body><div id="father"><a href="#">链接1号</a><a href="#">链接2号</a><a href="#">链接3号</a><a href="#">链接4号</a><a href="#">链接5号</a><a href="#">链接6号</a></div></body></html>
我们现在的需求是,希望鼠标点击每个 a 标签,都可以弹出“我是xxx”这样的提示。比如点击第一个 a 标签,弹出“我是链接1号”这样的提示。这意味着我们至少要安装 6 个监听函数给 6 个不同的的元素(一般我们会用循环,代码如下所示),如果我们的 a 标签进一步增多,那么性能的开销会更大。
// 假如不用代理模式,我们将循环安装监听函数const aNodes = document.getElementById('father').getElementsByTagName('a')const aLength = aNodes.lengthfor(let i=0;i<aLength;i++) {aNodes[i].addEventListener('click', function(e) {e.preventDefault()alert(`我是${aNodes[i].innerText}`)})}
考虑到事件本身具有“冒泡”的特性,当我们点击 a 元素时,点击事件会“冒泡”到父元素 div 上,从而被监听到。如此一来,点击事件的监听函数只需要在 div 元素上被绑定一次即可,而不需要在子元素上被绑定 N 次——这种做法就是事件代理,它可以很大程度上提高我们代码的性能。
事件代理的实现
用代理模式实现多个子元素的事件监听,代码会简单很多:
// 获取父元素const father = document.getElementById('father')// 给父元素安装一次监听函数father.addEventListener('click', function(e) {// 识别是否是目标子元素if(e.target.tagName === 'A') {// 以下是监听函数的函数体e.preventDefault()alert(`我是${e.target.innerText}`)}} )
在这种做法下,我们的点击操作并不会直接触及目标子元素,而是由父元素对事件进行处理和分发、间接地将其作用于子元素,因此这种操作从模式上划分属于代理模式。
