如果没有 `<slot />` 而是影子根,子节点会发生什么

What happens to child nodes if there is no `<slot />`, but a shadow root

考虑这段代码:

//js
class FooBar extends HTMLElement {
  constructor(){
    super();
  }
}

customElements.define('foo-bar', FooBar);


<!-- html -->
<foo-bar>
  <h1>Test</h1>
</foo-bar>

这将在浏览器中显示 »Test«。

如果构造函数改为:

constructor () {
  super();
  this.shadow = this.attachShadow({ mode: 'open' }) 
}

»Test« 消失,因为现在有影子根。

如果构造函数再改成

constructor () {
  super();
  this.shadow = this.attachShadow({ mode: 'open' });
  this.shadow.appendChild(document.createElement('slot')); 
}

»Test« 再次出现,因为现在 <foo-bar>

的所有子节点都有一个默认插槽

但是如果影子根中没有<slot />,子节点会发生什么。它们仍然出现在 this.children 中,其 style.display 属性 仍然是 ""。所以它们在 dom 内,但没有呈现,即使你 css 告诉相反?这里究竟发生了什么?

完整的详细说明位于:


<foo-bar>
  <h1>Test</h1>
</foo-bar>

H1 是 浅色DOM,
“添加”shadowDOM/root 内容是反映影子DOM,没动!!!

H1 始终保持明亮DOM :

  • invisible(在页面中)在lightDOM 对于元素 shadowDOM/root,

  • visible(在页面中)用于自定义元素 without shadowDOM/root

  • 除非您使用 appendChild 明确移动它(或任何 DOM 移动操作)

你说:所以它们在 dom 内,但没有渲染,即使你 CSS 告诉相反?

不,它们 渲染的,就像任何普通的 DOM 元素一样。只是不再可见了。

您可以通过在 lightDOM 中包含一个 SCRIPT 标签来进行测试。它会 render 并执行!


在下面的代码片段中

您引用 lightDOMthis.querySelector("span").innerHTML="weird";

但是用 this.shadowRoot.querySelector("span").innerHTML="weird";

引用 shadowDOM

有效吗,因为DIV(里面有SPAN)是黑盒

<template id="MY-ELEMENT">
  <style>
    :host {
      display: inline-block;
      font-family: Arial;
    }
    ::slotted(div){
      color:blue;
    }
    ::slotted(span){
      color:gold; /* alas, you can style the 'box', not elements inside */
    }
  </style>
  <h3><slot></slot></h3>
</template>
<style>
  span {
    background:lightcoral; /* from global/host CSS, style slotted content lightDOM */
  }
</style>
<script>
  customElements.define('my-element', class extends HTMLElement {
    constructor() {
      super().attachShadow({mode: 'open'})  
             .append(document.getElementById(this.nodeName).content.cloneNode(true));
    }
  });
</script>
<my-element>
  <div>Hello <span>Component</span> World!</div>
</my-element>

在 F12 开发工具中检查组件:

Chrome & Firefox:

DIV 不在 shadowDOM/root 中,在光线下不可见DOM
所有 elements/styles 将始终 反映 到 shadowDOM/root

点击'reveal'带你走向光明DOM

So to shadowDOM, slotted content is a black-box of elements & styles;
reflected from lightDOM
that is why ::slotted can only style the box, and not what is inside.

注意:在F12控制台编辑DIV,你会立即看到变化反映到阴影DOM


插槽和灯DOM 是实时连接

通过更改 <slot name=...>,您可以进行以前需要更多编码的交互(想想路线、选项卡、答案)(还记得那些 jQuery show/hide 天吗?)

<template id="MY-ELEMENT">
  Custom Element SLOTs are: 
  <slot name=answer></slot>
</template>
<style>
  img { /* style all IMGs in lightDOM */
    max-width: 100vw;
    max-height: 70vh;
  }
</style>
<script>
  customElements.define('my-element', class extends HTMLElement {
    connectedCallback() {
      this.attachShadow({mode: 'open'})
          .append(document.getElementById(this.nodeName).content.cloneNode(true));
      this.onclick = (evt) => {
           const answer = evt.composedPath()[0].innerText; // button label
           this.shadowRoot.querySelector('slot').name = answer;
           this.children[0].slot = answer;//include lightDOM buttons again
      }
    }
  });
</script>
<my-element>
  <span slot=answer><button>Cool</button><button><b>Awesome</b></button><button>Great</button></span>
  <div slot=Cool><img src="https://i.imgur.com/VUOujQT.jpg"></div>
  <span slot=Awesome> <h3>SUPER!</h3></span>
  <div slot=Awesome><img src="https://i.imgur.com/y95Jq5x.jpg"></div>
  <div slot=Great><img src="https://i.imgur.com/gUFZNQH.jpg"></div>
</my-element>


更多与 SLOT 相关的答案可以通过 Whosebug 搜索找到:Custom Elements SLOTs