2026-09-23 · 原文发布日
用交互实例讲透 Shadow DOM
Simon Willison 用 AI 工具生成交互式页面来讲解 Shadow DOM:通过可点击的实例演示样式隔离、继承、slots、parts 和 JavaScript 访问等概念,展示 shadow root 如何创建带私有样式表的独立 DOM 树。适合前端开发者或学习者直观理解这一机制,也可参考其“用一句话让 AI 生成教学 artifact”的做法。
原文要点
Simon Willison发布了一个讲解Shadow DOM的交互式页面,演示样式封装、继承、slots、parts与JavaScript访问,展示shadow root如何创建带私有样式表的隔离DOM树。该页面是他用一句话提示词让Fable 5.1 Medium生成的artifact:‘Build an artifact to explain shadow roots in CSS with interactive examples’。原文本身只含工具链接与这条提示词,没有展开技术内容。
这次做成什么
照作者的方法自己造一个可交互的Shadow DOM教学单页:用一句提示词让AI生成初版,再用本站给出的完整HTML校验并补足五个必讲概念,最后本地打开逐项点击验证。
准备什么
- 任意现代浏览器(Chrome/Edge/Firefox/Safari均可)
- 一个可生成HTML artifact的AI工具(如Claude/Fable类)
- 本地保存与打开单个HTML文件的权限,无需构建工具
具体怎么做
用一句话生成初版
在AI工具中输入作者同款提示词“Build an artifact to explain shadow roots in CSS with interactive examples”,让它输出一个单文件HTML页面。
对照五概念清单补齐
检查生成页是否覆盖:样式封装、继承、slots、parts、JavaScript访问。缺哪项,就把本站artifact中对应小节代码并入,或追问AI按该小节补齐。
本地运行并逐项点击
把最终HTML存为shadow-dom-demo.html双击打开,依次操作:修改宿主页全局样式看shadow内部是否不受影响、向slot传入不同内容、用part选择器从外部改样式、点击按钮触发通过shadowRoot引用执行querySelector('.inner')的访问,并观察控制台输出。
验证隔离边界
在页面里把宿主div的文字颜色改成红色,确认无shadow的普通段落变红而shadow内部段落颜色不变;再用浏览器DevTools查看shadow root在DOM树中的独立挂载点。
可直接复制的材料
shadow-dom-demo.html(可直接保存运行的教学页)
本站独立编写的完整示例,非原作者页面;覆盖样式封装、slot、part、JS访问四个交互点,继承概念在页面正文中由对比段落体现,可直接保存运行。
<!DOCTYPE html>
<html lang="zh">
<head><meta charset="utf-8"><title>Shadow DOM 交互演示</title>
<style>body{font-family:sans-serif} p{color:gray} .plain{color:green}</style>
</head>
<body>
<h1>Shadow DOM 五个概念</h1>
<p class="plain">这是普通段落,受页面全局样式控制(绿色)。</p>
<div id="host"></div>
<button id="jsBtn">3) 用JS读取shadow内部文本</button>
<button id="partBtn">4) 通过part从外部改按钮颜色</button>
<pre id="log"></pre>
<template id="tpl">
<style>
p{color:rebeccapurple} /* 私有样式:不受外部 p{color:gray} 影响 */
.btn{padding:4px 8px}
::slotted(span){font-weight:bold}
</style>
<p class="inner">1) 样式封装:我在shadow里,始终是紫色。</p>
<p>2) slot插槽:<slot name="msg">默认内容</slot></p>
<button class="btn" part="btn">part暴露的按钮</button>
</template>
<script>
const host=document.getElementById('host');
const root=host.attachShadow({mode:'open'});
root.appendChild(document.getElementById('tpl').content.cloneNode(true));
host.innerHTML='<span slot="msg">传入的slot内容</span>'+host.innerHTML;
document.getElementById('jsBtn').onclick=()=>{
document.getElementById('log').textContent='JS访问结果: '+root.querySelector('.inner').textContent;
};
document.getElementById('partBtn').onclick=()=>{
const st=document.createElement('style');
st.textContent='#host::part(btn){background:tomato;color:#fff}';
document.head.appendChild(st);
};
</script>
</body></html>输入与输出示例
本站虚构演示预期效果,未声称实际运行;不同浏览器渲染细节一致但默认字体可能略有差异。
输入
把上方HTML保存为shadow-dom-demo.html并在浏览器打开,点击“用JS读取shadow内部文本”按钮对应输出
页面表现:普通段落显示绿色;shadow内段落显示紫色(不受全局p{color:gray}影响);slot位置显示加粗的“传入的slot内容”;点击按钮后pre区域输出“JS访问结果: 1) 样式封装:我在shadow里,始终是紫色。”;再点part按钮后shadow内按钮变为番茄红底白字。做完检查这些
- shadow内部段落颜色为紫色且不随页面全局p样式变化
- 向slot传入的span内容替换默认内容且被加粗
- 点击JS按钮后日志区出现shadow内部文本,证明attachShadow(mode:open)可被外部访问
- part按钮生效,证明::part()可从外部穿透样式封装
- DevTools中能看到#host下的#shadow-root独立节点
容易卡住的地方
- attachShadow用mode:closed时外部querySelector拿不到内部节点,教学页需用open
- ::part()只能选中显式声明part属性的元素,不能任意穿透
- 全局CSS自定义属性(变量)会继承进shadow,颜色字面量则不会
- 原文只是工具链接加一句提示词,技术细节为本站补齐,勿当成原作者内容
- 部分AI生成页会漏掉parts或JS访问,需按清单逐项验收
本站借助AI整理来源事实,独立编写中文操作指南与示例。示例不代表实际运行结果;来源效果尚未由本站独立复现。付费模板与模型、服务费用分别计算。
- 原文标题
- Shadow roots, explained with live examples
- 原作者
- Simon Willison
- 原始发布日期
- 2026-09-23
- 来源
- https://simonwillison.net/2026/Sep/23/shadow-roots/