Shadow DOM 内部触发的事件默认无法穿透影子边界——影子边界类似一道防火墙,将事件限制在内部。若需外部监听,必须在创建事件时显式添加 composed: true。此时,event.target 指向宿主元素(例如 ),而真正的触发源位于 event.composedPath()[0] 中。
Shadow DOM 内部触发的事件默认无法穿透影子边界——影子边界类似一道防火墙,将事件限制在内部。若需外部监听,必须在创建事件时显式添加 composed: true。此时,event.target 指向宿主元素(例如 ),而真正的触发源位于 event.composedPath()[0] 中。手动派发事件时,务必声明 composed: true;原生事件(如 click、input)虽然默认 composed 为 true,但仅限于浏览器自主触发——通过 dispatchEvent() 派发时不会自动补充该属性。

长期稳定更新的攒劲资源: >>>点此立即查看<<<
composed: true 才能跨 Shadow BoundaryShadow DOM 内部触发的事件(例如点击 )默认不会冒泡到宿主元素外侧。事件重定向后,event.target 显示为宿主元素(如 ),但原始触发节点存储在 event.composedPath()[0] 中。希望事件被外部监听到(比如父组件响应子按钮点击)时,必须显式设置 composed: true:
new CustomEvent('click', { bubbles: true, composed: true }) —— 缺少 composed: true,外部 addEventListener 永远无法接收click、input)默认 composed 为 true,但仅限于浏览器原生触发;通过 dispatchEvent() 手动派发时,必须显式声明composed,如需兼容,需使用 polyfill 或改用属性变更 + attributeChangedCallback 间接通信event.composedPath() 是唯一可靠方式获取真实触发源在宿主元素上监听事件时,event.target 始终返回宿主本身(如 ),而非内部按钮。要获取实际点击的节点,只能依赖 event.composedPath():
event.composedPath()[0] 是原始触发元素(如 shadow 内的 )event.path —— 它是 Chrome 私有属性,Firefox/Safari 不一致,且已被废弃event.composedPath().includes(this.shadowRoot)event.target 做逻辑分支许多开发者在自定义元素上使用 this.addEventListener('click', e => { if (e.target.matches('button')) {...} }),这在 Shadow DOM 下会失效:
e.target 永远是 ,无法匹配 'button'this.shadowRoot.querySelector('button').addEventListener('click', ...)this.shadowRoot.addEventListener('click', e => { if (e.target.matches('[part="action"]')) { ... } })part 属性 + ::part() 样式 + 语义化事件分发composed: false 的实际用途:防止事件“泄露”到意外位置多数场景需要事件穿透,但某些内部行为不应暴露给外部——例如轮播组件自动切换时触发的 slide-change,仅用于内部状态同步:
composed: false 后,事件连宿主元素都无法接收,更别说外部容器this.shadowRoot.addEventListener('slide-change', ...)composed: false 不影响 bubbles,事件仍可在 shadow 内冒泡,但不出边界composed: true,再确认监听位置是否位于 shadow 内事件穿透不是开关,而是逐层显式控制的过程。每个事件需单独决定是否设置 composed、在哪里监听、如何获取源节点——没有全局开关,也没有默认的“智能转发”。
侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述