刚开始给元素绑事件的时候,我都是谁需要就给谁绑。直到遇到列表动态新增元素的问题,才了解到事件冒泡和事件委托。
事件流分三个阶段:捕获阶段、目标阶段、冒泡阶段。事件触发后,会从最外层 document 一层层往下传到目标元素(捕获),然后再一层层往上冒回 document(冒泡)。我们平时绑的事件,默认都是在冒泡阶段触发。
事件委托就是利用了冒泡机制。不给每个子元素单独绑事件,而是把事件绑在父元素上。
javascript
运行
// 传统方式:给每个li绑事件,新增的li没有事件
const lis = document.querySelectorAll('li');
lis.forEach(li => {
li.addEventListener('click', function() {
console.log(this.textContent);
});
});
// 事件委托:给父元素ul绑事件
const ul = document.querySelector('ul');
ul.addEventListener('click', function(e) {
// 判断点击的是不是li
if (e.target.tagName === 'LI') {
console.log(e.target.textContent);
}
});
点击子元素时,事件会冒泡到父元素,这时通过event.target判断点击的是哪个子元素,再做相应处理。
用事件委托的好处太多了:减少事件绑定数量,节省内存;动态新增的子元素也能自动拥有事件,不用重新绑定。我给待办事项列表改用了事件委托之后,新增的条目不用再单独绑定删除事件,代码精简了一大截。
当然也不是所有事件都能委托,比如 focus、blur 这些不冒泡的事件就不行。搞懂了事件机制的底层原理,遇到交互问题就能从根上分析,而不是靠瞎试。