关于 shadow-dom-overlay-insertion
shadow-dom-overlay-insertion 是关于在可能被放入 Shadow DOM 插槽的 Web 组件中插入覆盖层的技术参考。它说明如何选择插入容器,以及如何处理 Lit 渲染 Shadow DOM 前的时序间隙。
主要功能
- 01通过 `assignedSlot` 检测插槽组件
- 02将覆盖层插入 Shadow DOM 内部容器
- 03处理渲染前的插槽分配时序
- 04按 DOM 状态选择插入方式
适用场景
- 01为已插槽的 Web 组件添加覆盖层
- 02避免覆盖层进入宿主的 light DOM
- 03Lit 渲染后重试插入
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ editframe/ skills --skill shadow-dom-overlay-insertion安装后在 Claude Code 中输入/shadow-dom-overlay-insertion确认已加载。
详细介绍
Shadow DOM 覆盖层插入
问题
当组件 A(例如 EFCanvas)尝试在 DOM 中将覆盖层插入到自身旁边时,通常会使用 this.parentElement.appendChild(overlay)。当 A 被插槽嵌入 Shadow DOM 宿主(例如 EFWorkbench)时,这会出问题。
EFWorkbench (shadow host)
shadow root
<div class="canvas-slot-container"> ← the real DOM parent
<slot name="canvas"> ← slot element
[EFCanvas is assigned here] ← light DOM, assigned via slot
efCanvas.parentElement返回的是Shadow DOM 宿主(EFWorkbench),而非内部容器- 调用
shadowHost.appendChild(overlay)会将覆盖层放入宿主的轻量 DOM 中,Shadow DOM 中的 CSS 和 z-index 不会生效,因此覆盖层不可见
修复模式
private insertOverlay(overlay: HTMLElement): void {
const slot = this.assignedSlot;
if (slot) {
// Correctly slotted: insert into the shadow-internal container
slot.parentElement?.appendChild(overlay);
return;
}
if (this.parentElement?.shadowRoot) {
// Shadow DOM exists but slot assignment hasn't happened yet
// (connectedCallback fires before Lit renders shadow DOM).
// Return early; caller should retry via requestAnimationFrame.
return;
}
// Not slotted — plain DOM, safe to use parentElement
this.parentElement?.appendChild(overlay);
}
时序隐患
解析 HTML 时,connectedCallback 会在 Lit 渲染 Shadow DOM 之前触发。此时:
this.assignedSlot为null(尚未分配插槽)this.parentElement不为null(它是 Shadow DOM 宿主)this.parentElement.shadowRoot也为null(Shadow DOM 尚未附加)
在同一微任务周期稍后(Lit 执行 performUpdate 后):
this.assignedSlot会变为<slot>元素slot.parentElement是内部容器
保护模式:
connectedCallback() {
super.connectedCallback();
this.tryInsertOverlay();
}
private tryInsertOverlay(): void {
if (!this.assignedSlot && this.parentElement?.shadowRoot) {
// Too early — shadow host exists but slot not yet assigned.
// Lit will assign the slot after performUpdate.
requestAnimationFrame(() => this.tryInsertOverlay());
return;
}
this.insertOverlay(this.overlay);
}
规则摘要
| 情况 | assignedSlot | parentElement.shadowRoot | 操作 |
|---|---|---|---|
| 未通过插槽嵌入 | null | null/undefined | 使用 parentElement.appendChild |
| 正确通过插槽嵌入 | 非 null | (任意) | 使用 assignedSlot.parentElement.appendChild |
| 时机过早(Lit 尚未渲染) | null | 非 null | 提前返回,通过 RAF 重试 |
| 已通过插槽嵌入,父级没有 Shadow DOM | null | null | 使用 parentElement.appendChild |
当父级是 Shadow DOM 宿主时,切勿直接使用 this.parentElement。
评论
支持 Markdown