当你点击一个元素,事件不是只在那个元素上触发。它从树的顶端往下传到目标(capture 阶段),在目标本身触发(target 阶段),再往上传回顶端(bubble 阶段)。默认你加的监听器在 bubble 阶段执行,这就是为什么点子元素也会触发祖先的 handler。用 capture 模式加监听器,则会改在下行途中执行。
在监听器里调用 stopPropagation,会让事件不再往下一层传递,剩下的层就不会被触发。它不会挡掉同一元素上的其他监听器——那是 stopImmediatePropagation 的工作。在上方选一个中断点,看触发顺序被截短。
关于这个主题的常见疑问与实用解答。
它们是同一趟旅程的两个方向。capture 由上而下,从最外层祖先往目标走,发生在抵达目标之前。bubble 由下而上,从目标往最外层祖先走。用一般方式加的监听器在 bubble 阶段执行;给 addEventListener 第三个参数传 true,就改在 capture 阶段执行。
因为冒泡。事件在子元素(目标)上触发后,会往上传回每一层祖先,沿路执行各层的 bubble 阶段监听器。这常常有用——让容器上的一个 handler 就能回应任何子元素的点击——但会让以为事件只停在被点元素上的人意外。
少用为妙,只在子元素要处理事件、且真的必须阻止祖先也反应时用——例如可点击卡片里的一颗按钮,不该同时触发卡片的点击。太常用它通常代表事件设计可以更简单;它也可能弄坏其他正当依赖事件传到共同祖先的代码。