事件委托就是把子元素的事件绑到父元素上,利用冒泡自动处理新加的子元素。
核心三步:
① 父元素绑事件——addEventListener
② event.target——找到真正点击的是谁
③ 判断标签/class——只处理目标元素
坑:不委托 change/focus 这类不冒泡的事件、target 是实际点击的、currentTarget 是绑事件的。
点菜单项: 冒泡到 ul, 被点的会亮, 下面同步代码与结论。
// 笨办法:每个 li 都绑一次
// 先把所有 li 一锅端抓出来
let allLis = document.querySelectorAll('#menu li');
// 从第 0 个,挨个走一遍
for (let i = 0; i < allLis.length; i++) {
// 这次轮到的这一个 li
let oneLi = allLis[i];
// 给它单独绑一个点击
oneLi.addEventListener('click', show);
}
// 数一下:绑了 N 次,很啰嗦
console.log('绑了多少个', allLis.length);
// 问题:以后新加的 li 没绑上,还得再绑一遍,浪费
// 聪明办法:只给爹 ul 绑一次
// 只抓到爹 ul 一个
let menuUl = document.getElementById('menu');
// 点 ul 里任何东西都会冒泡到这
menuUl.addEventListener('click', function (e) {
// 取出"到底点中了谁"
let pointEl = e.target;
// 打出来看看是谁
console.log('点中了', pointEl.nodeName, pointEl.innerText);
// 看快递单:点中的是不是 li
if (pointEl.nodeName === 'LI') {
// 是 li,才往下办正事
console.log('这是个 li,办事');
}
});
① 事件会冒泡:点 li,一路冒到爹 ul;② 爹只绑了一个监听;③ 在监听里用 e.target 看一眼"点的是不是 li",是才办事。
如果你忘了判断 e.target 是不是 li 就直接办事,可能点到空白处也触发。因为 ul 里除了 li 还有空格、padding 这些;所以要先看 nodeName === 'LI'(或用 matches)。
因为监听绑在爹 ul 上,新 li 再怎么加,都是 ul 的孩子,点它照样冒泡到 ul——根本不用重新绑。这就是委托最大的好处。
① 用在哪:聊天消息、商品列表、待办项等动态生成的列表点击。
② 常见坑:忘了判断 e.target 是不是目标标签,点空白也触发;新元素忘了绑定。
③ 怎么解决:给父绑一次,用 e.target.matches()/nodeName 判断;新元素自动生效不用重绑。
| API | 作用 | 参数 | 返回值 | 代码示例 |
|---|---|---|---|---|
| 父元素.addEventListener | 只给爹绑一次(委托) | 事件名/函数 | 无 | ul.addEventListener('click', fn); |
| e.target | 点中的具体子元素 | 无 | 元素 | let el = e.target; |
| e.target.nodeName | 判断点的是不是目标标签 | 无 | 大写标签名 | if (e.target.nodeName === 'LI') fn(); |