:host:defined doesn't work, :host(:defined) works [duplicate]

六月ゝ 毕业季﹏ 提交于 2020-01-30 07:00:15

问题


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>&lt;${this.tagName.toLowerCase()}&gt;</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>&lt;${this.tagName.toLowerCase()}&gt;</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 nothing

The :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:

  1. :host matches the <x-foo> element.

  2. x-foo matches nothing.

  3. .foo matches only the element.

  4. .foo:host matches nothing

  5. :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>&lt;${this.tagName.toLowerCase()}&gt;</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>&lt;${this.tagName.toLowerCase()}&gt;</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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!