提问人:OfirD 提问时间:10/12/2022 更新时间:10/13/2022 访问量:287
如何将嵌套的影子 DOM 放在彼此的顶部?
How to position nested shadow DOMs on top of each other?
问:
是否可以将嵌套的影子 DOM 放在彼此的顶部?
使用普通 DOM 时,嵌套的 div 默认只是堆叠:
.d1 {
width: 150px;
height: 150px;
background-color: lightseagreen;
}
.d2 {
width: 100px;
height: 100px;
background-color: darkslateblue;
}
.d3 {
width: 50px;
height: 50px;
background-color: lightgray;
}
<div class="container">
<div class="d1">
<div class="d2">
<div class="d3">
</div>
</div>
</div>
</div>
但是有了影子 DOM,这怎么能做到这一点呢?
id = 0;
depth = 3;
customElements.define('box-component', class extends HTMLElement {
constructor() {
super();
if (id > depth)
return;
this.id = id++;
var template = document.getElementById("box-template");
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = template.innerHTML;
// The only goal of all of the following boilerplate is to set 'exportparts' attribute,
// such that each parent exports its *descendants* (not just *children*) parts.
// This is done by accessing the shadow dom of the child from its parent.
// (note: the outermost component doesn't need to export its parts)
if (id == 0)
return;
const parts = [...this.shadowRoot.children]
.filter(elem => elem.getAttribute('part'))
let thisExportParts = null;
let childExportParts = null;
thisExportParts = parts.map(elem => [
[elem.getAttribute('part'), `${elem.getAttribute('part')}${this.id}`].join(':')
].join(','));
if (this.shadowRoot && this.shadowRoot.children) {
const childShadowRoot =
[...this.shadowRoot.children].filter(elem => elem.shadowRoot)[0];
if (childShadowRoot) {
const fullChildExportParts = childShadowRoot.getAttribute('exportparts');
if (fullChildExportParts) {
childExportParts = fullChildExportParts.split(',').map(part => {
return part.includes(':') ? part.split(':')[1] : part;
}).join(',');
}
}
}
this.setAttribute('exportparts', [thisExportParts, childExportParts].filter(_ => _).join(','));
}
});
box-component::part(box1) {
width: 150px;
height: 150px;
background-color: lightseagreen;
}
box-component::part(box2) {
width: 100px;
height: 100px;
background-color: darkslateblue;
}
box-component::part(box3) {
width: 50px;
height: 50px;
background-color: lightgray;
}
<template id="box-template">
<style>
</style>
<div part="box"></div>
<box-component></box-component>
</template>
<box-component class="container"></box-component>
答:
1赞
OfirD
10/12/2022
#1
有可能。在影子 DOM 中,应添加以下内容:
:host {
position: absolute;
top: 0;
}
此规则将文档中自定义组件元素(影子主机)的所有实例的样式设置为绝对定位,同时还使定位相对于每个定位的父元素(这就是需要的原因)。:host
top: 0
实际上,这类似于将问题中的普通 DOM 代码更改为使用定位(注意,如果没有定位,仅设置是行不通的(进一步阅读)):top: 0
.container {
position: relative;
}
.container div {
position: absolute;
top: 0;
}
.d1 {
width: 150px;
height: 150px;
background-color: lightseagreen;
}
.d2 {
width: 100px;
height: 100px;
background-color: darkslateblue;
}
.d3 {
width: 50px;
height: 50px;
background-color: lightgray;
}
<div class="container">
<div class="d1">
<div class="d2">
<div class="d3">
</div>
</div>
</div>
</div>
所以,这是完整的代码:
id = 0;
depth = 3;
customElements.define('box-component', class extends HTMLElement {
constructor() {
super();
if (id > depth)
return;
this.id = id++;
var template = document.getElementById("box-template");
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = template.innerHTML;
// The only goal of all of the following boilerplate is to set 'exportparts' attribute,
// such that each parent exports its *descendants* (not just *children*) parts.
// This is done by accessing the shadow dom of the child from its parent.
// (note: the outermost component doesn't need to export its parts)
if (id == 0)
return;
const parts = [...this.shadowRoot.children]
.filter(elem => elem.getAttribute('part'))
let thisExportParts = null;
let childExportParts = null;
thisExportParts = parts.map(elem => [
[elem.getAttribute('part'), `${elem.getAttribute('part')}${this.id}`].join(':')
].join(','));
if (this.shadowRoot && this.shadowRoot.children) {
const childShadowRoot =
[...this.shadowRoot.children].filter(elem => elem.shadowRoot)[0];
if (childShadowRoot) {
const fullChildExportParts = childShadowRoot.getAttribute('exportparts');
if (fullChildExportParts) {
childExportParts = fullChildExportParts.split(',').map(part => {
return part.includes(':') ? part.split(':')[1] : part;
}).join(',');
}
}
}
this.setAttribute('exportparts', [thisExportParts, childExportParts].filter(_ => _).join(','));
}
});
box-component::part(box1) {
width: 150px;
height: 150px;
background-color: lightseagreen;
}
box-component::part(box2) {
width: 100px;
height: 100px;
background-color: darkslateblue;
}
box-component::part(box3) {
width: 50px;
height: 50px;
background-color: lightgray;
}
<template id="box-template">
<style>
:host {
position: absolute;
top: 0px;
}
</style>
<div part="box"></div>
<box-component></box-component>
</template>
<box-component class="container"></box-component>
评论
exportparts
的进一步阅读