如何将插槽子项附加到 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项需要绝对定位
为此,我们需要
- 创建叠加层
- 从 Web 组件中删除插槽元素
- 将它们附加到叠加元素。
- 提供正确的定位
要设置完美的叠加层,我需要创建一个 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);
这种方法的问题:
- 我注意到从插槽中删除
assignedNodes
会弄乱很多事情。
- 当我尝试移动插槽的 lightDOM
时,很多东西都不起作用
一旦元素被移动,slotchange
就不起作用了。
- 此外,Web 组件可以在任何框架中使用,它可以是
lit-html
、Vue
甚至是普通的 JavaScript
。我注意到在移动这些 DOM 元素后,这开始破坏消费者库的抽象。
此需求绝对适用于任何 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 而不是纯字符串...
我正在创建一个下拉菜单 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项需要绝对定位
为此,我们需要
- 创建叠加层
- 从 Web 组件中删除插槽元素
- 将它们附加到叠加元素。
- 提供正确的定位
要设置完美的叠加层,我需要创建一个 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);
这种方法的问题:
- 我注意到从插槽中删除
assignedNodes
会弄乱很多事情。 - 当我尝试移动插槽的 lightDOM 时,很多东西都不起作用 一旦元素被移动,
slotchange
就不起作用了。- 此外,Web 组件可以在任何框架中使用,它可以是
lit-html
、Vue
甚至是普通的JavaScript
。我注意到在移动这些 DOM 元素后,这开始破坏消费者库的抽象。
此需求绝对适用于任何 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 而不是纯字符串...