JavaScript 事件监听的学习笔记(冒泡和事件委托)

网页要"点一下有反应",靠的就是事件监听。这篇记一下我理解的事件冒泡和事件委托。

基本用法

const button = document.querySelector('#my-button');

button.addEventListener('click', function () {
  alert('被点到了');
});

第一个参数是事件类型,常见的有 clickinputkeydownmouseover 这些。

事件冒泡

点了一个按钮,点击事件不只发生在按钮上,还会一层一层往父元素传上去,像水底冒泡一样:

<div id="outer">
  <button id="inner">点我</button>
</div>

给 outer 和 inner 都绑了 click,点按钮的时候两个都会触发,先是 inner,然后是 outer。如果不想让它往上冒,可以在处理函数里调用:

button.addEventListener('click', function (event) {
  event.stopPropagation(); // 阻止继续往上冒
});

事件委托:一个顶一百个

列表有很多项,每项都要能点击。傻办法是给每个 li 都绑一个监听;聪明的办法是绑在父元素 ul 上,靠冒泡统一接住:

const list = document.querySelector('#todo-list');

list.addEventListener('click', function (event) {
  // event.target 就是实际被点的那个元素
  if (event.target.tagName === 'LI') {
    event.target.classList.toggle('done');
  }
});

这样写还有个额外的好处:以后用 JS 新加进来的列表项,不用重新绑监听也能响应点击,因为它压根不需要自己有监听。

我踩过的坑

有一次我在循环里给 20 个按钮绑事件,想在处理函数里用循环变量 i,结果点哪个都弹出 20。后来明白是因为函数执行的时候循环早就跑完了。解决办法是把 const 写在循环里(每次循环一个新变量),或者干脆用事件委托。

小结

事件委托这个思路让我印象很深:把处理放在"总入口",再去看具体是谁触发的。猜数字小练习里的按键处理就是这样写的。