Shadow DOM 中直接在 innerHTML 写 <style> 或 <link> 无效,因浏览器将其当文本解析或忽略;须用 document.createElement('style') 注入,或 CSSStyleSheet + adoptedStyleSheets(Safari 17.4+ 支持);继承属性需 :host { all: initial; font-family: inherit; color: inherit; } 控制,事件冒泡默认开启,::part() 替代 ::slotted() 精准样式化。

直接在 shadowRoot.innerHTML 里写 <style> 或塞 <link rel="stylesheet">,样式不会生效,还会污染全局——这不是配置问题,是 Shadow DOM 的隔离机制在起作用。
为什么 shadowRoot.innerHTML = '<style>...</style>' 不生效
浏览器会把 <style> 当作纯文本节点解析,不进入 CSSOM,规则无法参与样式计算。后续 JS 尝试读取 sheet.cssRules 会得到空数组,甚至触发 FOUC(闪白)。
- 必须用
document.createElement('style')创建节点,再设textContent,最后shadowRoot.appendChild(style) - 禁止拼接用户输入到
textContent,否则 XSS 风险直通影子树 - 若需复用基础重置样式,提前创建
CSSStyleSheet实例,再赋给shadowRoot.adoptedStyleSheets
<link rel="stylesheet"> 在 shadowRoot 里完全无效
Shadow DOM 规范明确禁止 <link> 在影子树中触发 fetch 和样式解析——它只是个普通 DOM 节点,link.sheet 永远为 null,控制台也无报错。
- 不要动态
document.createElement('link')再appendChild到shadowRoot,毫无意义 - 也不要指望构建工具能识别这类引用:Webpack/Vite 看不到它,无法哈希、压缩或做依赖分析
- 真正可用的现代方案是
CSSStyleSheet+adoptedStyleSheets,但 Safari 17.4+ 才稳定支持,旧版需降级
如何安全注入第三方 HTML + CSS + JS
不能靠字符串拼接完事。必须拆解处理,确保每类资源都落在正确的作用域内。
立即学习“前端免费学习笔记(深入)”;
- 把完整 HTML(含
<style>、<script>、<link>)写进<template>标签 - 用
template.content.cloneNode(true)拷贝节点树 - 遍历克隆结果:
shadowRoot.appendChild(style)处理每个<style>;对每个<script>,新建document.createElement('script')并appendChild到shadowRoot - 遇到
<link rel="stylesheet">?提取href,用fetch加载 CSS 文本,再通过CSSStyleSheet.replaceSync()注入
容易被忽略的继承泄漏和事件冒泡
Shadow DOM 不自动隔离 font-family、color、line-height 这些可继承属性。宿主页面设了 body { font-family: "PingFang SC"; },影子里的 <p> 依然会继承。
- 在影子根最外层加
:host { all: initial; font-family: inherit; color: inherit; }—— 注意all: initial会清掉display,得手动补上display: block或对应值 - 事件默认
composed: true,第三方按钮点击仍会冒泡到主文档;如需阻断,显式调用dispatchEvent(new CustomEvent('xxx', { composed: false })) -
::slotted()只影响传入<slot>的顶层节点,且仅支持可继承属性;想控制内部按钮大小?得用part="button"+::part(button)显式暴露钩子


















