【问题标题】:What happens to child nodes if there is no `<slot />`, but a shadow root如果没有`<slot />`,但有影子根,子节点会发生什么
【发布时间】:2020-05-17 13:03:11
【问题描述】:

考虑这段代码:

//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« 再次出现,因为现在&lt;foo-bar&gt; 的所有子节点都有一个默认槽

但是如果影子根中没有&lt;slot /&gt;,子节点会发生什么。它们仍然出现在this.children 中,其style.display 属性仍然是""。所以它们在dom内,但没有渲染,即使你的css告诉相反的?这里到底发生了什么?

【问题讨论】:

    标签: javascript web-component custom-element


    【解决方案1】:

    完整详解在:::slotted CSS selector for nested children in shadowDOM slot


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

    H1 ​​是 lightDOM
    “添加”shadowDOM/ root 内容反映shadowDOM,没有移动!!!

    H1 始终保留在 lightDOM 中:

    • lightDOM 中的不可见(在页面中)对于 with shadowDOM/root 的元素,
    • 可见(在页面中)用于自定义元素没有 shadowDOM/root

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

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

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

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


    在下面的代码sn-ps中

    你用this.querySelector("span").innerHTML="weird";引用lightDOM

    但是用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 中,在 lightDOM 中保持不可见
    所有元素/样式将始终反映到 shadowDOM/root

    点击“显示”将带您进入 lightDOM

    所以 shadowDOM,开槽内容是元素和样式的黑盒;
    反映来自 lightDOM
    这就是为什么::slotted 只能设置盒子的样式,而不能设置里面的样式。

    注意:在 F12 控制台中编辑该 DIV,您会立即看到更改反映到 shadowDOM


    SLOT 和 lightDOM 是实时连接

    通过更改&lt;slot name=...&gt;,您可以进行以前需要更多编码的交互(想想路由、选项卡、答案)(还记得那些 jQuery 显示/隐藏的日子吗?)

    <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 相关答案可以通过 StackOverflow 搜索找到:Custom Elements SLOTs

    【讨论】:

    • 非常感谢这个复杂的答案!所以attachShadow »光的开关«,所以光 DOM 中包含的元素变得不可见?有没有一种 JS 方法来测试给定的元素在这种情况下是否可见?
    • +1 for 电灯开关,我要用那个!您可以使用slotchange 事件:developer.mozilla.org/en-US/docs/Web/API/HTMLSlotElement/… 监控 SLOT 更改事件...tada..。如果你在 StackOverflow 搜索框中输入:WCSLOT,你会得到我所有的 SLOT 答案
    • 嘿嘿,知道了也好!但是,如果我引用了 h1 (document.querySelector('h1')),是否有任何属性告诉我这个元素不可见,因为它的父元素有一个没有插槽的 shadowRoot,或者换句话说:h1 本身不是开槽?
    • AFAIK 不是(但我每天都在学习,我会研究这个),SLOT 标签和 slot/name 属性建立了联系。 lightDOM 中的内容只是 lightDOM 中的内容,只是盒子里的垃圾。您可以让 WebComponent 根据 SLOT 中的内容检查其 lightDOM。
    • 这是 2016 年的背景/深入 Github 问题讨论,来自 Google (hayatoito) 和 Apple (rniwa) 的开发人员在 slotchange 上发表评论:github.com/w3c/webcomponents/issues/504
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-24
    • 1970-01-01
    • 2011-03-19
    • 2019-09-27
    • 2022-01-04
    • 2018-11-11
    相关资源
    最近更新 更多