在下面的示例中,我尝试根据
**host**
是否具有具有 empty
属性的开槽元素来设置其样式。如果它确实有这样的元素,那么我希望添加一个柠檬绿边框:
class Component extends HTMLElement {
constructor() {
super().attachShadow({mode:'open'});
const template = document.getElementById("TEMPLATE"); this.shadowRoot.appendChild(template.content.cloneNode(true));
}
}
window.customElements.define('wc-foo', Component);
<template id="TEMPLATE">
<style>
:host(:has(::slotted([empty]))) {
border: 2px solid lime;
}
</style>
<div>Custom web component should have a lime border
</div>
<slot></slot>
</template>
<wc-foo>
<div empty>"Empty Div"</div>
</wc-foo>
但是这不起作用,我不知道为什么。猜测可能是因为
:host()
选择器必须是一个简单的选择器。还有其他方法可以实现吗?
PS:这个问题不是如何使用“:host”(或“:host()”)和“:has()”的重复,因为这是关于选择主机的孩子,而我正在尝试选择主机依赖于它的子节点。
在撰写此答案时,
:host(:has(...))
选择 light DOM 仅在 Safari 中实现。
因此以下示例目前仅适用于 Safari:
class WcFoo extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: 'open'});
this.shadowRoot.innerHTML = `
<style>
:host {
display: block;
margin: 1em;
}
:host(:has([empty])) {
border: 4px solid lime;
}
</style>
<div>Custom web component should have a lime border</div>
<slot></slot>
`;
}
}
customElements.define('wc-foo', WcFoo);
<h3>Preview this example using Safari</h3>
<wc-foo>
<div empty>"Empty Div"</div>
</wc-foo>
<wc-foo>
<div>"No empty attribute"</div>
</wc-foo>
在研究这个问题时,我发现了一些可能提供更多背景信息的 GitHub 问题:https://github.com/web-platform-tests/interop/issues/208。 这个仅限 Safari 的答案也来自 Westbrook 的 GitHub 评论:https://github.com/w3c/webcomponents-cg/issues/5#issuecomment-1220786480
您可以将属性反映到主机上以设置主机的样式。要检测您的元素已被包含
empty
属性的元素插入槽,您可以使用 slotchange
事件。
在 slotchange 事件中将样式属性反映到主机上,并使用选择器::host([empty])
添加石灰边框。
class WcFoo extends HTMLElement {
constructor() {
super();
this.attachShadow({
mode: 'open'
});
this.shadowRoot.innerHTML = `
<style>
:host {
display: block;
margin: 1em;
}
:host([empty]) {
border: 4px solid lime;
}
</style>
<div>Custom web component</div>
<slot></slot>
`;
this.shadowRoot.querySelector('slot').addEventListener('slotchange',
(evt) => {
const hasEmpty = evt.target.assignedElements()
.some(el => el.matches('[empty]'));
if (hasEmpty) {
this.setAttribute('empty', '');
} else {
this.removeAttribute('empty');
}
}
);
}
}
customElements.define('wc-foo', WcFoo);
<wc-foo>
<div empty>"Has lime border"</div>
</wc-foo>
<wc-foo>
<div>"Will not have border"</div>
</wc-foo>