Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

tabs-panel (web component)

17 views
Skip to first unread message

luserdroog

unread,
Jul 10, 2023, 10:23:09 PM7/10/23
to
A <tabs-panel /> accepts contents for two slots, named "tab" and "content". The tabs are shown spaced out and clickable, controlling their associated content.

Critique or comments welcome.

tabs.html:
<html>
<body>
<tabs-panel>
<div slot="tab" data-target="#out1" class="active tab">first</div>
<div slot="tab" data-target="#out2" class="tab">second</div>
<div slot="tab" data-target="#out3" class="tab">third</div>
<div id="out1" slot="content" class="active content">Content 1: the first</div>
<div id="out2" slot="content" class="content">Content 2: which follows after</div>
<div id="out3" slot="content" class="content">Content 3: where it all ends</div>
</tabs-panel>
</body>
<script src="tabs.js"></script>
</html>



tabs.js:
const tabsPanelTemplate = document.createElement( "template" );
tabsPanelTemplate.innerHTML = `
<style>
.tabs{
display: flex;
justify-content: flex-start;
}
::slotted([slot="tab"]) {
cursor: pointer;
padding: 1ex;
margin: 1ex;
}
::slotted([slot="tab"].active) {
border: solid 1px;
}
::slotted([slot="content"]){
display: none;
}
::slotted([slot="content"].active){
display: block;
}
</style>
<slot name="tab" class="tabs"></slot>
<hr>
<slot name="content"></slot>
`;

class TabsPanel extends HTMLElement {
constructor() {
super();
this.attachShadow( {mode:"open"} );
this.shadowRoot.appendChild( tabsPanelTemplate.content.cloneNode(true) );
}

connectedCallback() {
this.querySelectorAll('[slot="tab"]').forEach( tab => {
tab.addEventListener( "click", e => {
this.querySelectorAll("[slot]").forEach( tab => tab.classList.remove( "active" ) );
tab.classList.add( "active" );
this.querySelector( tab.dataset.target ).classList.add( "active" );
})
});
}

disconnectedCallback() {
this.querySelectorAll('[slot="tab"]').forEach( tab => {
tab.removeEventListener();
});
}
};

window.customElements.define( "tabs-panel", TabsPanel );

Michael Haufe (TNO)

unread,
Jul 11, 2023, 1:40:30 AM7/11/23
to
On Monday, July 10, 2023 at 9:23:09 PM UTC-5, luserdroog wrote:
> A <tabs-panel /> accepts contents for two slots, named "tab" and "content". The tabs are shown spaced out and clickable, controlling their associated content.
>
> Critique or comments welcome.
> [...]

You can leverage <details /> for this too. Something like:

<article class="tabs">
<details class="tab">
<summary class="tab-label">Tab 1</summary>
<div class="tab-content">Content 1</div>
</details>
<details class="tab">
<summary class="tab-label">Tab 2</summary>
<div class="tab-content">Content 2</div>
</details>
<details class="tab">
<summary class="tab-label">Tab 3</summary>
<div class="tab-content">Content 3</div>
</details>
</article>

<style>
.tabs {
position: relative;
display: inline-flex;
border: 1px solid blue;
height: 300px;
}

.tab[open] {
background-color: #ccc;
}

.tab-label {
padding: 1em;
border: 1px solid red;
border-radius: 15% 15% 0 0;
}

.tab-content {
position: absolute;
background-color: cyan;
left: 0;
height: calc(100% - 3.1em);
width: 100%;
overflow: auto;
}
</style>

You'll just want to toggle all sibling tabs when one is open by intercepting the 'toggle' event. Here's a quick codepen:

<https://codepen.io/mlhaufe/pen/abQVrWg>

luserdroog

unread,
Jul 11, 2023, 9:35:29 AM7/11/23
to
On Tuesday, July 11, 2023 at 12:40:30 AM UTC-5, Michael Haufe (TNO) wrote:
> On Monday, July 10, 2023 at 9:23:09 PM UTC-5, luserdroog wrote:
> > A <tabs-panel /> accepts contents for two slots, named "tab" and "content". The tabs are shown spaced out and clickable, controlling their associated content.
> >
> > Critique or comments welcome.
> > [...]
>
> You can leverage <details /> for this too. Something like:
>

That's pretty cool. <defensive reaction>I think there are real advantages
of abstraction, encapsulation, and packaging with the web component
approach. All the functionality is wrapped up in the component.
The application doesn't have to do a thing beyond constructing the html.
</defensive reaction>

The clickable words should probably be <button>s for accessibility.

Michael Haufe (TNO)

unread,
Jul 11, 2023, 12:53:08 PM7/11/23
to
That was the hope on WebComponents, but I was burned bad by them and wrote a popular article criticizing them a handful of years ago:

<https://thenewobjective.com/web-development/a-criticism-of-web-components>

Things have improved, but not by much sadly

> The clickable words should probably be <button>s for accessibility.

The <summary/> element already has an aria role of "button" so there is no need:

<https://developer.mozilla.org/en-US/docs/Web/HTML/Element/summary#technical_summary>
0 new messages