事件委托
委托就是把原本属于自己做的事情委托(交给)别人去做
原理:利用事件冒泡机制把本该在元素自身响应的事件交给任意一个上级元素去处理,这种行为称之为事件委托或者事件代理。
听起来似乎有点滑稽,自己该做的不做,居然让“上级”做!但在编程领域,这是一个非常有用的技巧。
应用场景
- 多个同级元素同一种事件绑定
例如:1个 div 下有5个 button 要绑定 click 事件,且做同一件事,打印helloWorld。如果给5个 button 都绑定事件,那太浪费内存了,可以利用事件委托,给父级div节点绑定来响应事件。
HTML:
<div>
<button id="button1">button1</button>
<button id="button2">button2</button>
<button id="button3">button3</button>
<button id="button4">button4</button>
<button id="button5">button5</button>
</div>
JS:(不使用事件委托)
let buttons = document.querySelectorAll('button')
for(let i = 0; i < buttons.length; i++){
buttons[i].addEventListener('click',fn)
}
function fn(e) {
console.log(e.target)
console.log(e.currentTarget)
}
JS:(使用事件委托)
// 这里只是用委托原理解释委托的好处,但此代码并不十分正确
// 正确代码请看后面
function fn(e) {
console.log(e.target)
console.log(e.currentTarget)
}
let div = document.querySelector('div')
div.addEventListener('click',fn)
- 监听不存在的元素
例如:元素 div 还未被创建出来,但又想事先就对他实现监听。
事件委托的好处
以上两种方式都可以使用事件委托来实现事件监听。它们的优点是:1、省内存;2、可以监听动态元素,不论内部元素是增加还是减少。
封装事件委托函数
知晓原理,封装一个事件委托函数试试吧!
function on(eventType,elDelegate,selector,fn){
if(!(elDelegate instanceof Element) && (typeof elDelegate === 'string')){
elDelegate = document.querySelector(elDelegate)
}
elDelegate.addEventListener(eventType,(e) => {
let el = e.target
while(!el.matches(selector)){ // 注意:matches接收的是CSS选择器
if(elDelegate === el){
el = null
break
}
el = el.parentNode
}
el && fn.call(el,e,el)
})
return elDelegate
}
// 使用方式
on('click','div','#btn1',fn)
注意:
- 仅仅判断 target 是否匹配 selector是不够的,因为如果 selector内还有子元素的话,只进行一次判断是不够的。还需要向上递归判断target的上一个节点是否是 selector,直到找到或者匹配到代理节点后结束匹配;
matches()
函数参数接收的是CSS选择器,不要误传元素对象;e.target
是在页面中被用户操作的元素,e.currentTarget
是程序员监听的元素。