如何将插槽子项附加到 HTML 正文标签以进行绝对定位?

How to append slot children to HTML body tag for absolute positioning?

我正在创建一个下拉菜单 Web 组件,供消费者使用,例如:

<custom-menu>
    <custom-menu-anchor>
        <button>Toggle Menu</button>
    </custom-menu-anchor>

    <custom-menu-item>Fish</custom-menu-item>
    <custom-menu-item>
        <custom-icon name="chicken"/>
        <span>Chicken</span>
    </custom-menu-item>
</custom-menu>

这里像<custom-menu-items>这样的slot项需要绝对定位

为此,我们需要

  1. 创建叠加层
  2. 从 Web 组件中删除插槽元素
  3. 将它们附加到叠加元素。
  4. 提供正确的定位

要设置完美的叠加层,我需要创建一个 overlay/surface 元素并删除 custom-menu-item 个子元素并将它们全部附加到叠加元素。

为了实现这一点,我在 connectedCallback 生命周期方法中尝试了类似下面的方法:

const slot = this.shadowRoot.querySelector('slot');
const surface = document.createElement('div');

const nodes = slot.assignedNodes();

surface.append(...nodes);
document.body.appendChild(surface);

这种方法的问题:

此需求绝对适用于任何 positioned/offset 组件,例如通知、对话框、下拉菜单、Snackbar 和上述方法显然很困难,而且肯定不是一种更简洁的做事方式。

我们如何才能更有效地避免所有提到的副作用?

移动灯光 dom 节点通常不是一个好主意 - 您的用户(和浏览器)希望它们保持原样。

如果您想在其他地方呈现内容,您可以将内容作为属性提供。

lit-html 的简化示例(使用它通过 . 设置 属性)

<my-el .content=${'<div>my content</div>'}></my-el>

然后在您的自定义元素中,您需要对 dom.

中尚不存在的字符串执行某些操作
class MyEl extends ... {
  onContentChanged() {
    document.body.querySelector('my-el-target').innerHTML = this.content;
  }
}

PS:这过于简单化了——实际上你可能想要 create/manage connectedCallback 上的 my-el-target——或者让它由一个完整的控制器来处理。此外,"template" 可能应该是 lit-template 而不是纯字符串...