使用 attachshadow() 前需确认元素支持(如 div、custom-element),不可用于 img、input 等替换元素;mode 仅可选 'open'(可访问 shadowroot)或 'closed'(shadowroot 返回 null);一个元素仅能挂载一个 shadow root,重复调用抛 invalidstateerror;shadow root 支持 innerhtml 和 appendchild,但 script 不自动执行,样式不穿透,事件默认不冒泡至 light dom。

用 attachShadow() 创建 shadow root 的前提条件
不是所有元素都能挂载 shadow DOM。attachShadow() 只能在原生支持 shadow host 的元素上调用,比如 div、span、article、custom-element(自定义元素),但不能在 img、input、script 等替换元素(replaced elements)或语义受限元素(如 br、hr)上使用。调用前最好先检查元素是否支持:
if (element.attachShadow) {
const shadow = element.attachShadow({ mode: 'open' });
}
否则会直接抛出 DOMException: Failed to execute 'attachShadow' on 'Element': This element cannot host shadow root。
mode: 'open' 和 mode: 'closed' 的实际区别
mode 是唯一必需的配置项,它决定 shadow root 是否可被外部 JS 访问:
-
mode: 'open':创建后可通过element.shadowRoot直接读取,调试器里也能展开查看结构; -
mode: 'closed':element.shadowRoot返回null,即使你保存了返回值,外部也无法通过元素反查到该 shadow root —— 这不是“加密”,只是切断了标准访问路径,不防主动持有引用的代码。
注意:closed 模式下,如果忘了保存 attachShadow() 的返回值,就真的彻底丢失对 shadow DOM 的控制权了。
常见错误:重复调用 attachShadow() 导致 InvalidStateError
一个元素只能有一个 shadow root。第二次调用 attachShadow() 会立即报错:
Uncaught DOMException: Failed to execute 'attachShadow' on 'Element': Shadow root has already been attached
避免方式:
- 调用前判断:
if (!element.shadowRoot)(仅对open模式可靠); - 更稳妥的做法是统一管理,比如只在自定义元素的
constructor或connectedCallback中执行一次; - 不要用
innerHTML = ''或removeChild()试图“清空” shadow root —— 它不能被替换,只能整个移除元素再重建。
和 innerHTML、appendChild() 搭配使用的注意事项
shadow root 创建后,它就是一个标准的 DocumentFragment,支持所有常规 DOM 操作:
- 可以直接赋值:
shadow.innerHTML = '<p>Hello</p>'; - 也可以用
appendChild()、append()等方法插入节点; - 但要注意:往 shadow root 里插入 script 标签不会执行(除非手动
eval或创建script元素并设置type="module"); - 样式必须内联写在 shadow 内(或用
<style></style>),外部 CSS 默认不穿透。
真正容易被忽略的是:如果你在 shadow 内监听事件(比如按钮点击),又在外部用 element.addEventListener('click', ...),两者互不影响 —— shadow 内部事件默认不冒泡到 light DOM,除非显式设置 bubbles: true 并且没被 stopPropagation() 拦截。










