问题
Is it not possible, or not allowed, to combine :host and :defined in CSS, while combining the latter with the :host() pseudoclass works?
As you can see in below example, the following
:host:defined { display: block; }
does not work, while
:host(:defined) { display: block; }
works.
class CustomElement extends HTMLElement {
constructor() {
super();
this.shadow = this.attachShadow({ mode: 'closed' });
const css = `
:host { display: none; }
:host:defined { display: block; }
`;
this.styles = document.createElement('style');
this.styles.innerHTML = css;
}
connectedCallback() {
const div = document.createElement('div');
div.innerHTML = `<code><${this.tagName.toLowerCase()}></code> connected!`;
this.shadow.appendChild(this.styles);
this.shadow.appendChild(div);
}
}
class OtherCustomElement extends HTMLElement {
constructor() {
super();
this.shadow = this.attachShadow({ mode: 'closed' });
const css = `
:host { display: none; }
:host(:defined) { display: block; }
`;
this.styles = document.createElement('style');
this.styles.innerHTML = css;
}
connectedCallback() {
const div = document.createElement('div');
div.innerHTML = `<code><${this.tagName.toLowerCase()}></code> connected!`;
this.shadow.appendChild(this.styles);
this.shadow.appendChild(div);
}
}
customElements.define('custom-element', CustomElement);
customElements.define('other-custom-element', OtherCustomElement);
<custom-element></custom-element>
<other-custom-element></other-custom-element>
The above code example on codepen: https://codepen.io/connexo/pen/GRKEGax
回答1:
From the specification we can read:
The
:host
pseudo-class, when evaluated in the context of a shadow tree, matches the shadow tree’s shadow host. In any other context, it matches nothingThe
:host()
function pseudo-class has the syntax::host( <compound-selector-list> )
When evaluated in the context of a shadow tree, it matches the shadow tree’s shadow host if the shadow host, in its normal context, matches the selector argument. In any other context, it matches nothing.
Basically, :host
will match the shadow host and nothing more. You cannot combine it with any other selector While the second syntax allow you to add a selector inside ()
.
If you refer to the example shown in the specification:
say you had a component with a shadow tree like the following:
<x-foo class="foo">
<"shadow tree">
<div class="foo">...</div>
</>
</x-foo>
For a stylesheet within the shadow tree:
:host
matches the<x-foo>
element.
x-foo
matches nothing.
.foo
matches only the element.
.foo:host
matches nothing
:host(.foo)
matches the element.
Note the (2) and the (4). (2) is selecting nothing because no common selector can select outside the shadow tree. Only :host
and :host()
can do. The (4) is selecting nothing because :host
is designed to be used alone to select the shadow host but if you want to add another selector you have to use :host()
like in the (5).
Here is a basic example to illustrate:
class CustomElement extends HTMLElement {
constructor() {
super();
this.shadow = this.attachShadow({ mode: 'closed' });
const css = `
:host.box { color:red; }
`;
this.styles = document.createElement('style');
this.styles.innerHTML = css;
}
connectedCallback() {
const div = document.createElement('div');
div.innerHTML = `<code><${this.tagName.toLowerCase()}></code> connected!`;
this.shadow.appendChild(this.styles);
this.shadow.appendChild(div);
}
}
class OtherCustomElement extends HTMLElement {
constructor() {
super();
this.shadow = this.attachShadow({ mode: 'closed' });
const css = `
:host(.box) { color:red }
`;
this.styles = document.createElement('style');
this.styles.innerHTML = css;
}
connectedCallback() {
const div = document.createElement('div');
div.innerHTML = `<code><${this.tagName.toLowerCase()}></code> connected!`;
this.shadow.appendChild(this.styles);
this.shadow.appendChild(div);
}
}
customElements.define('custom-element', CustomElement);
customElements.define('other-custom-element', OtherCustomElement);
<custom-element class="box"></custom-element>
<other-custom-element class="box"></other-custom-element>
Now the question is: Why we have two kind of selectors when we can simply have :host
combined with any other selector.
It's to avoid confusion and ambiguity when parsing the selector since the shadow host can only be selected by a special selector. If we write :host.foo
the browser will try to match the element with .foo
and :host
but it will be tricky because .foo
can only match an element inside the shadow tree while :host
can go outside so parsing the selector to find if yes or no we have :host
inside in order to consider the remaining part of the selector to match the shadow host will be tedious.
Using :host()
make it easy for the browser to parse the selector and :host
is a particular case of :host()
with no selector.
Note: This is different from the specificity of similar pseudo-classes, like
:matches()
or:not()
, which only take the specificity of their argument. This is because:host
is affirmatively selecting an element all by itself, like a "normal" pseudo-class; it takes a selector argument for syntactic reasons (we can’t say that :host.foo matches but .foo doesn’t), but is otherwise identical to just using :host followed by a selector.
Note the we can’t say that :host.foo
matches but .foo
doesn’t
来源:https://stackoverflow.com/questions/57713398/hostdefined-doesnt-work-hostdefined-works