如果中间有一个中间元素,我可以插入 Shadow DOM 内容吗?

Can I have slotted Shadow DOM content if I have an intermediate element in between?

通过 Shadow DOM (v1) 示例 in this tutorial,它定义了一个 Web 组件(选项卡),其中每个选项卡对应一个命名和默认插槽:

<fancy-tabs>
  <button slot="title">Title</button>
  <button slot="title" selected>Title 2</button>
  <button slot="title">Title 3</button>
  <section>content panel 1</section>
  <section>content panel 2</section>
  <section>content panel 3</section>
</fancy-tabs>

它会呈现为:

<fancy-tabs>
  #shadow-root
    <div id="tabs">
      <slot id="tabsSlot" name="title">
        <button slot="title">Title</button>
        <button slot="title" selected>Title 2</button>
        <button slot="title">Title 3</button>
      </slot>
    </div>
    <div id="panels">
      <slot id="panelsSlot">
        <section>content panel 1</section>
        <section>content panel 2</section>
        <section>content panel 3</section>
      </slot>
    </div>
</fancy-tabs>

为了保留现有的 API,我想创建一个与此类似的组件,但我可以在其中创建每个选项卡作为其自己的自定义元素。所以 API 看起来像:

<fancy-tabs>
  <fancy-tab>
    <button slot="title">Title</button>
    <section>content panel 1</section>
  </fancy-tab>
  <fancy-tab>
    <button slot="title" selected>Title 2</button>
    <section>content panel 2</section>
  <fancy-tab>
  <fancy-tab>
    <button slot="title" selected>Title 3</button>
    <section>content panel 3</section>
  <fancy-tab>
</fancy-tabs>

但是让它渲染成与上面类似的阴影 DOM。

换句话说,我想要的是像 <fancy-tab> 这样的中间元素,同时仍然控制它下面的插槽元素。我已经尝试将 <fancy-tab> 创建为具有开放 shadowRoot 的 CE,创建为没有 shadowRoot 的 CE,并且根本不将其定义为自定义元素。

有没有办法做到这一点? 或者插槽 位于光的第一个 child DOM?

具有 slot 属性的元素必须是 Light DOM.

的第一个子元素

所以如果你想保留你的第三段代码的结构,你可以使用 ,每个都有阴影 DOM.

<fancy-tabs> 组件将获取 <fancy-tab><fancy-tab> 组件将抓取内容。


实际上,要创建一个 "tabs" 组件,您甚至不必定义影子的子组件 DOM(但您当然可以满足自定义需求)。

这是一个最小的 <fancy-tabs> 自定义元素示例:

customElements.define( 'fancy-tabs', class extends HTMLElement 
{
    constructor()
    {
        super()
        this.btns = this.querySelectorAll( 'button ')
        this.addEventListener( 'click', this )
        this.querySelector( 'button[selected]' ).focus()
    }

    handleEvent( ev ) 
    {
        this.btns.forEach( b => 
        {
            if ( b === ev.target ) 
                b.setAttribute( 'selected', true )
            else
                b.removeAttribute( 'selected' )
        } )
    }
} )
fancy-tabs {
    position: relative ;
}

fancy-tab > button {
    border: none ;
}

fancy-tab > section {
    background: #eee ;
    display: none ;
    position: absolute ; left: 0 ; top: 20px ;
    width: 300px ; height: 75px ;
}

fancy-tab > button[selected] + section {
    display: inline-block  ;
}
<fancy-tabs>
    <fancy-tab>
        <button>Title 1</button>
        <section>content panel 1</section>
    </fancy-tab>
    <fancy-tab>
        <button selected>Title 2</button>
        <section>content panel 2</section>
    </fancy-tab>
    <fancy-tab>
        <button>Title 3</button>
        <section>content panel 3</section>
    </fancy-tab>
</fancy-tabs>