← 返回目录

12. 事件委托原理与实战

事件委托就是把子元素的事件绑到父元素上,利用冒泡自动处理新加的子元素。

核心三步:
父元素绑事件——addEventListener
event.target——找到真正点击的是谁
判断标签/class——只处理目标元素

坑:不委托 change/focus 这类不冒泡的事件、target 是实际点击的、currentTarget 是绑事件的

12.1 互动演示(只给 ul 绑一次,新来的也能用)

点菜单项: 冒泡到 ul, 被点的会亮, 下面同步代码与结论。

12.2 知识点逐条讲解

① 笨办法 vs 聪明办法

// 笨办法:每个 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 判断;新元素自动生效不用重绑。

一句话记住:给爹绑一次,靠冒泡把事件传上来,用 e.target 判断点中的是不是目标;少绑事件,新来的元素自动能用。

本页重点

API作用参数返回值代码示例
父元素.addEventListener只给爹绑一次(委托)事件名/函数ul.addEventListener('click', fn);
e.target点中的具体子元素元素let el = e.target;
e.target.nodeName判断点的是不是目标标签大写标签名if (e.target.nodeName === 'LI') fn();