事件委托原理:事件捕获与事件冒泡。通俗来说就是找一个代理人做本来自己该做的事。
下面引入两个场景:
场景一:你要给100个按钮添加点击事件,咋办?
答:监听这100个按钮的祖先,等冒泡的时候判断target是不是这100个按钮中的一个。
场景二:你要监听目前不存在元素的点击事件,咋办?
答:监听这个元素的祖先,等点击的时候再看看是不是我想要监听的元素。
场景一代码
//html部分
<div id='div1'>
<button>click 1</button>
<button>click 2</button>
……
<button>click 100</button>
</div>
//js部分
div1.addEventListener('click',(e)=>{
const t=e.target //用一个变量获取当前被点击的元素
if(t.tagName.toLowerCase()==='button') //t的标签名转小写后为button
console.log('button的内容是'+t.textContent)
})
如果要给多个元素绑定事件,可能需要用到循环,且若为同一事件则十分浪费内存空间。对此,事件委托的第一个优点就是:省内存。
场景二代码
//html部分
<div id='div1'><div>
//js部分
setTimeout(()=>{
const btn=document.createElement('button')
btn.textContent='click1'
div1.appendChild(btn)
},1000) //设置一个1s后的定时器,往div中添加按钮
div1.addEventListener('click',(e)=>{
const t=e.target
if(t.tagName.toLowerCase()==='button'){
console.log('button被点击了')
}
})
如果要给还未出现的元素绑定事件,必须用到事件委托,因为这时运用常规的DOM方法或者JQuery方法是获取不到元素的,因为定时器的队列是放在“稍后”执行的。对此,事件委托的第二个优点就是:可以监听动态元素。
小总结:事件委托两大优点:省内存;可动态。
封装一个事件委托函数
上述的场景中对于特定的场景写特定的代码,这个就叫做写业务。(这个就是日常的程序猿做的事情)但如果你能把业务代码抽象出来,写一套代码解决该类场景所有类似问题,那你就牛逼了,这个行为叫做造轮子。
//用户点击#div1里的button时,调用fn函数
//具体业务场景
on('click','#div1','button',()=>{
console.log('button被点击了')
})
//轮子,封装一个on函数
function on(eventType, element, selector, fn) { //传进4个参数,为事件类型,元素,选择器,自定义函数
if (!(element instanceof Element)) { //若element不是一个元素
element = document.querySelector(element) //获取元素
}
element.addEventListener(eventType, (e) => {
const t = e.target
if (t.matches(selector)) { //判断一个元素是否满足一个选择器,返回值为bool
fn(e) //调用函数
}else{
console.log('别点了,这个元素没有监听器')
}
})
}
一个疑问:JS支持事件吗?
答:不支持,DOM事件不属于JS的功能,属于浏览器提供的DOM功能(含冒泡,捕获等),JS只是调用了DOM提供的addEventListener这个API接口而已。
再次回到事件委托
本来是孩子自己要做的事情(添加侦听器),却交给了爸爸来做(在爸爸身上添加侦听器),这个就叫做事件委托。