Skip to content

事件是怎么传到监听函数的? ​

事件先从 window 走到目标(捕获),在目标上触发,再走回 window(冒泡)。监听默认挂在冒泡阶段。列表和表格用父节点监听,靠 event.target 认出真正被点中的元素,这就是事件委托。

原理 ​

addEventListener(type, handler, true) 的第三个参数为 true 时,监听放在捕获阶段。传对象时用 { capture: true, once: true, passive: true }。

  • event.target:实际触发事件的元素。
  • event.currentTarget:当前正在执行监听的元素,和函数里的 this 通常是同一个。
  • stopPropagation():后面的捕获或冒泡不再继续。
  • stopImmediatePropagation():同一元素上还没跑的监听也不跑。
  • preventDefault():取消浏览器默认行为,例如链接跳转、表单提交。它不停止传播。

委托适合子节点很多、或者子节点会增减的结构:监听只绑一次,新节点不用重新绑定。不冒泡的事件不能靠父节点收到,例如 focus、blur,要用捕获,或者改听会冒泡的 focusin / focusout。

passive: true 告诉浏览器这个监听不会 preventDefault。滚动监听标成 passive,浏览器可以立刻滚动,不必等监听函数跑完。

追问 ​

委托有什么代价?

一层父节点要自己过滤 target,复杂交互容易把判断写乱。点在子元素内部时,target 可能是按钮里的图标,要用 closest 找到真正的按钮。

React 的事件也是这样吗?

React 把合成监听委托到根容器,再用自己的事件对象分发。原生监听和 React 监听的 stopPropagation 不在同一套链上,混用时原生监听仍然能先看到事件。

易错点 ​

  • preventDefault 不会阻止冒泡,stopPropagation 也不会取消默认行为。
  • target 和 currentTarget 在委托时不是同一个元素。
  • mouseenter 不冒泡,mouseover 会冒泡。

相关 ​