事件委托
简单的来说,事件委托就是把一个元素响应事件的函数委托到另外一个元素上。
一般会把一个元素或者一组元素的事件委托到它的父层或更外层的祖先元素上。
真正绑定事件的元素是外层元素,当事件响应到需要绑定的元素上时,会通过事件的冒泡机制从而触发绑定在外层元素上的事件,然后在外层元素上执行函数。
举个例子:
一个大学宿舍里有A、B、C、D四个人,到中午了大家要去食堂打饭,他们可以每个人都自己下楼去买。但是那样比较累,于是A、B、C同学委托D同学帮他们带饭,完了D同学到食堂买了饭后,再到宿舍一一发给另外三位同学。
这里带饭也就是一个事件,A、B、C就是需要响应事件的DOM元素,帮大家统一出去带饭的D同学就是代理元素,是真正绑定这个事件的元素。
事件对象
每次我们触发一个事件的时候,JS都会生成一个事件对象,这个事件对象就是对该事件的一些描述信息。
比如:我们出发一个点击事件的时候,你点在哪个位置了,点击的地方的坐标是多少;
// html:
<div id="dad">
<button id="son">按钮</button>
</div>
// js:
dad.onclick = function(event){
console.log(event)
}
点击后查看控制台:
里面就描述了这次鼠标事件的详细信息;
这里事件对象的名称不一定非要写成event,可以叫写成别的,只要知道指的是谁就行,我个人喜欢写成e;
target和currentTarget
事件对象里有target和currentTarget属性,这两者需要搞清楚区别:
// html:
<div id="dad">
<button id="son">按钮</button>
</div>
// js:
son.addEventListener('click',(e)=>{
console.log(e.target) // <button id="son">按钮</button>
console.log(e.currentTarget) // <button id="son">按钮</button>
})
dad.addEventListener('click',(e)=>{
console.log(e.target) // <button id="son">按钮</button>
console.log(e.currentTarget) // <div id="dad">...</div>
})
对 son 的监听中,点击了按钮,target和currentTarget都是son这个按钮; 对 dad 的监听中,点击了按钮,target还是son这个按钮,currentTarget却变成了dad这个div元素;
由此可知:
- target指的是被用户操作的元素,而currentTarget是被开发者添加函数监听的元素;
- target和currentTarget可能是同一个东西,也可能不是同一个东西;
事件委托应用场景
场景1
// html:
<div id="test">
<button>btn 1</button>
<button>btn 2</button>
...
<button>btn 99</button>
<button>btn 100</button>
</div>
要求给这100个按钮都添加点击事件,怎么做?
- 最慢的办法,一个一个添加,但是那样会累死的;
- 遍历循环,然后给遍历的每个按钮添加,这样可以;
- 事件委托;
事件委托代码:
const test = document.querySelector('#test')
test.addEventListener('click',(e)=>{
console.log('你点击了' + e.target.innerText)
})
场景2
// html:
<div id='test'></div>
// js:
setTimeout(()=>{
const button = document.createElement('button')
button.innerText = '按钮'
test.appendChild(button)
},1000)
// 一秒钟后,在div里添加一个按钮
要监听一个目前还不存在的元素的点击事件,怎么做?
如果元素还没生成就直接监听,那肯定是会出错的,这里还是要用事件委托:
test.addEventListener('click',(e)=>{
const userClick = e.target
if(userClick.matches('button')){ //(userClick.tagName.toLowerCase() === 'button')也可以
console.log('你点击了'+userClick.innerText)
}
})
// 监听祖先元素,然后用matches()的API来看看是不是我们原先想要监听的元素
事件委托的好处
我们每添加一个监听器,都会在内存里占一个内存,如果用事件委托的话,就可以节省内存空间了;
用事件委托可以监听目前不存在的元素,或者说是动态生成的元素;