事件重定向是指shadow dom中事件冒泡跨边界时,浏览器自动将event.target重设为shadow host而非内部真实触发元素,以保障封装性;真实目标需通过event.composedpath()[0]获取,自定义事件须设composed: true方可穿透边界。

在 Web Components 中,事件冒泡穿过 Shadow DOM 边界时,并不会像普通 DOM 那样直接把 event.target 传给外部监听器,而是被浏览器自动重定向(Retargeting)——外部看到的目标变成 shadow host 元素本身,而不是内部真实触发事件的元素。
什么是事件重定向(Event Retargeting)
这是 Shadow DOM 的核心行为之一:当一个事件从 shadow tree 冒泡出来,到达 light DOM 时,浏览器会把 event.target “伪装”成 shadow host,以隐藏内部实现细节。这保证了封装性——外部脚本无需知道组件内部结构,也不该依赖它。
- 内部监听器拿到的是真实目标(比如 shadow 内的
<button></button>) - 外部监听器拿到的是 host 元素(比如
<user-card></user-card>),哪怕点击的是 shadow 里的按钮 - 这个重定向只发生在跨 shadow boundary 的冒泡阶段,不改变事件传播路径本身
哪些事件会被重定向,哪些不会
重定向只对“源自 shadow tree”的事件生效;light DOM 中的元素(如 slot 插槽内容)触发的事件,不重定向。
- ✅ 点击 shadow 内部的
<span></span>→ 外部看到event.target === user-card - ✅ 点击 shadow 内部的
<input>→ 外部看到 host,不是 input - ❌ 点击 slotted 的文本(如
<user-card>John</user-card>中的 "John")→ 外部仍看到<span></span>或文本节点,因为它是 light DOM 的一部分
如何获取原始触发目标
如果你确实需要在外部访问真实目标(比如做精细化交互或调试),可以用 event.composedPath():
-
event.composedPath()[0]是事件最初触发的元素(即使它在 shadow 内) -
event.composedPath()返回完整冒泡路径,包含 shadow root、host、父 light DOM 节点等 - 注意:
composedPath()在非合成事件(如原生 click)中可用,但某些自定义事件需显式设置{ composed: true }
自定义事件要注意 composed 属性
手动 dispatch 的自定义事件默认不穿透 shadow boundary,除非显式开启:
-
new Event('my-event', { bubbles: true, composed: true })→ 可冒泡出 shadow,且被重定向 -
composed: false(默认)→ 事件止步于 shadow root,外部监听不到 - 没有
composed: true,即使bubbles: true,事件也“卡”在 shadow 内部
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











