因为原生click事件默认composed: false,穿过Shadow boundary时被截断,无法到达document;需手动派发bubbles: true且composed: true的CustomEvent才能穿透,event.composedPath()[0]可获取真实触发元素。

为什么 document.addEventListener('click') 收不到 Shadow 内部的点击?
因为原生 click 事件默认 composed: false,它在穿过 Shadow boundary 时就被截断了,根本不会到达 document。这不是监听器写错了,是浏览器的封装设计——内部事件不自动“透出”。
常见错误现象:document.querySelector('.my-btn') 拿不到 shadow 内按钮;绑在 document 上的 click 回调从不触发;用 event.target 判断点击位置总失败。
- 别试图给原生事件加
composed: true:属性只读,JS 无法修改 - 别依赖
event.target:它已被重定向为宿主元素(如<my-card>),不是真实点击的<button> - 检查是否漏了
bubbles: true:即使设了composed: true,没开冒泡也出不来
如何让事件真正穿透到 Light DOM?
唯一可靠方式是手动派发一个 CustomEvent,并同时设置 bubbles: true 和 composed: true。这是可控出口,原生事件没这个权限。
示例:在 shadow 内部点击按钮后通知外部
立即学习“前端免费学习笔记(深入)”;
使用 Puppeteer + Chrome 将 HTML 渲染为中文 PDF,自动处理图表等待、Tab 展开、动画、测高、白边消除、防分页,适用于看板、报表、网页和交互图表转 PDF。
const btn = this.shadowRoot.querySelector('button');
btn.addEventListener('click', () => {
this.dispatchEvent(new CustomEvent('submit', {
bubbles: true,
composed: true, // 缺它就停在 boundary
detail: { value: 'ok' }
}));
});
- 外部监听无需特殊处理:
document.addEventListener('submit', handler)直接生效 - 事件名建议用短小、语义明确的字符串(如
ui-change),避免和原生事件同名 - 不要在宿主元素上写
onclick="...":拿到的是重定向后的target,逻辑易错乱
怎么拿到 Shadow 内部的真实点击元素?
event.composedPath()[0] 是唯一稳定方式。它绕过重定向机制,始终返回原始触发节点,哪怕该节点嵌套在多层 Shadow 中。
调试时可这样验证:
host.addEventListener('submit', (e) => {
console.log('real target:', e.composedPath()[0]); // 真实按钮
console.log('retargeted target:', e.target); // 宿主元素
});
-
e.composedPath().includes(myInternalButton)比e.target === myInternalButton更安全 - IE 不支持
composedPath(),若需兼容,只能退回到组件内暴露方法或用MutationObserver动态绑定 - React ≤16 的合成事件系统会受重定向影响,推荐统一走
composed: true自定义事件通道
slot 里的事件为什么也收不到?
通过 <slot> 投影进来的节点,其事件仍受 Shadow boundary 约束。视觉上在组件内部,但事件流不自动穿透——浏览器不管“看起来在哪”,只认 DOM 树归属。
典型场景:父组件传入一个 <button slot="action">删除</button>,希望点击后通知父级。
- 不能指望外部按钮的
click自动冒泡出来 - 必须在 shadowRoot 中监听
slotchange,再对slot.assignedNodes()里的交互节点手动绑定,并转发为composed: true事件 -
::slotted(*)只控制样式,不影响事件行为;:host伪类也无法捕获 slot 内部事件
composed 这个硬性开关——它不像 bubbles 那样只管路径长度,而是直接决定“能不能跨过那道墙”。


















