前端实现事件委托,依赖于 DOM 事件模型的冒泡机制。由于事件的捕获冒泡机制,我们可以在父元素上添加一类事件监听器,达到监听父元素及其所有子元素所有关于此事件的目的。
优点
- 省监听数节约内存
- 可以监听动态元素
普通实现
一般如果需要作为委托对象的父元素内部子元素很单纯,如 ul 仅嵌套 li 元素的场景。以鼠标点击事件为例,我们一般可以这样写:
ul.addEventListener('click', function(e){
if(e.target.tagName.toLowerCase() === 'li') {
fn() // 执行某个函数
}
})
但是,如果 li 里面还有 span 元素,那么此时用户如果点击的是 li 里面的 span 元素,就没法触发 fn,这显然达不到监听所有子元素的效果。
完备实现
了解了上面一种实现的缺陷,我们就可以知道改进的思路了。显然我们需要判断用户操作对象的祖先元素里是否有 ul 元素里面的 li 元素,如果有就应该执行对应的委托函数,所以我们可以写出下面的代码:
<ul>
<li><span>1</span></li>
<li><span>2</span></li>
<li><span>3</span></li>
</ul>
function delegate(element, eventType, selector, fn) {
element.addEventListener(eventType, e => {
let el = e.target;
while (!el.matches(selector)) {
if (element === el) {
el = null;
break
}
el = el.parentNode;
}
el && fn.call(el, e, el);
})
return element;
}
delegate(document,'click','li',(e)=>console.log(e))