mcpField
S
shadow-dom-overlay-insertion

shadow-dom-overlay-insertion

github.com/editframe开发1971 次安装2 周前更新
代码仓库

关于 shadow-dom-overlay-insertion

shadow-dom-overlay-insertion 是关于在可能被放入 Shadow DOM 插槽的 Web 组件中插入覆盖层的技术参考。它说明如何选择插入容器,以及如何处理 Lit 渲染 Shadow DOM 前的时序间隙。

主要功能

  1. 01通过 `assignedSlot` 检测插槽组件
  2. 02将覆盖层插入 Shadow DOM 内部容器
  3. 03处理渲染前的插槽分配时序
  4. 04按 DOM 状态选择插入方式

适用场景

  1. 01为已插槽的 Web 组件添加覆盖层
  2. 02避免覆盖层进入宿主的 light DOM
  3. 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);
}

规则摘要

情况assignedSlotparentElement.shadowRoot操作
未通过插槽嵌入nullnull/undefined使用 parentElement.appendChild
正确通过插槽嵌入非 null(任意)使用 assignedSlot.parentElement.appendChild
时机过早(Lit 尚未渲染)null非 null提前返回,通过 RAF 重试
已通过插槽嵌入,父级没有 Shadow DOMnullnull使用 parentElement.appendChild

当父级是 Shadow DOM 宿主时,切勿直接使用 this.parentElement。

评论

支持 Markdown