事件冒泡 vs 事件捕获
Event bubbling vs Event capturing
一句话:一次点击在 DOM 里走三段 —— 先从 window 往下到目标(捕获), 在目标上触发(目标阶段),再从目标往上回到window(冒泡)。捕获从外到内,冒泡从内到外。
addEventListener 第三个参数决定你在哪一段听: 默认 false 听冒泡,true(或 { capture: true })听捕获。
为什么默认是冒泡?因为绝大多数时候你想知道的是 「用户点了什么」,从内往外传最自然 —— 而且这才让事件委托成为可能(见 #289)。
会追问:「怎么阻止?」
e.stopPropagation()—— 别再往上(或往下)传了。e.stopImmediatePropagation()—— 连同一个元素上的其他监听器都别跑了。e.preventDefault()—— 阻止的是默认行为(链接跳转、表单提交), 和传播完全无关。这两个最容易混,别答错。
还会追问:「有哪些事件不冒泡?」——focus、blur、load、mouseenter、mouseleave。 需要委托时用它们的冒泡版:focusin /focusout / mouseover /mouseout。
In one line: a click travels the DOM in three phases — down from window to the target (capture), fires on the target, then back up to window (bubble). Capture goes outside-in, bubbling goes inside-out.
The third argument to addEventListener picks the phase you listen in: false (the default) is bubbling, true (or { capture: true }) is capture.
Why is bubbling the default? Because what you almost always want to know is “what did the user click”, and inside-out is the natural direction for that — it is also what makes event delegation possible (see #289).
Follow-up: “How do you stop it?”
e.stopPropagation()— stop travelling further up (or down).e.stopImmediatePropagation()— also skip other listeners on the same element.e.preventDefault()— cancels the default behaviour (link navigation, form submit) and has nothing to do with propagation. These two get mixed up most often; do not confuse them.
Also asked: “Which events do not bubble?” —focus, blur, load, mouseenter, mouseleave. When you need to delegate, use their bubbling counterparts: focusin / focusout / mouseover / mouseout.