Going through the Shadow DOM (v1) example in this tutorial, it defines a Web Component (tabs), wherein each Tab corresponds to a named and default slot:
<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>
And it would render to this:
<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>
In order to preserve an existing API, I'd like to create a component similar to this, but one where I can create each Tab as its own Custom Element. So an API that looks like:
<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>
But have it render to similar Shadow DOM as above.
In other words, what I'd like is to have is an intermediate element like <fancy-tab>
, while still controlling the slot elements below it. I've tried creating <fancy-tab>
as a CE with an open shadowRoot, as a CE with no shadowRoot, and not defining it as a custom element at all.
Is there a way to do this? Or do slots have to be at the first child of the Light DOM?
Elements with a slot
attribute have to be first childs of the Light DOM.
So if you want to keep the structure of your 3rd piece of code, you could use nested custom elements, each with shadow DOM.
The <fancy-tabs>
component will grab the <fancy-tab>
.
The <fancy-tab>
component will grab the content.
Actually to create a "tabs" component you don't even have to define sub-components of shadow DOM (but you can for customization needs of course).
Here's a minimal <fancy-tabs>
custom-element example:
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>