Tablist not switching tabpanels (or tabs)
Opened this issue ยท 2 comments
Deleted user commented
<center>
<div class="window" style="width: 600px">
<div class="title-bar">
<div class="title-bar-text">Explorer</div>
<div class="title-bar-controls">
<button aria-label="Minimize"></button>
<button aria-label="Maximize"></button>
<button aria-label="Close"></button>
</div>
</div>
<div class="window-body">
<menu role="tablist">
<button aria-selected="true" aria-controls="about">About</button>
<button aria-controls="favorites">Favorites</button>
</menu>
<article role="tabpanel" id="about">
<h2>Hi ๐</h2>
<p>I'm AcaiBerii. I program mostly in c# but I do JavaScript and Java too.</p>
</article>
<article role="tabpanel" hidden id="favorites">
<p><strong>Music genres</strong> EDM, Lo-fi</p>
<p><strong>Languages</strong> C#, Java, JavaScript, Nemerle, Python</p>
<p><strong>Editors</strong> Visual Studio, VSCode, Intellij Idea, PyCharm</p>
</article>
</div>
</div>
</center>
When viewing this, it won't switch tabs or tabpanels.
This also won't let you interact with the tabs at all.
raspberrypisig commented
I came across this problem today. Looking at the documentation page, need some javascript to make tabs work.
In the HTML, before the closing body tag, add
<script src="script.js"></script>
Then in script.js, add
document.addEventListener("DOMContentLoaded", function() {
const tabs = document.querySelectorAll("menu[role=tablist]");
for (let i = 0; i < tabs.length; i++) {
const tab = tabs[i];
const tabButtons = tab.querySelectorAll("menu[role=tablist] > button");
tabButtons.forEach((btn) =>
btn.addEventListener("click", (e) => {
e.preventDefault();
tabButtons.forEach((button) => {
if (
button.getAttribute("aria-controls") ===
e.target.getAttribute("aria-controls")
) {
button.setAttribute("aria-selected", true);
openTab(e, tab);
} else {
button.setAttribute("aria-selected", false);
}
});
})
);
}
});
function openTab(event, tab) {
const articles = tab.parentNode.querySelectorAll('[role="tabpanel"]');
articles.forEach((p) => {
p.setAttribute("hidden", true);
});
const article = tab.parentNode.querySelector(
`[role="tabpanel"]#${event.target.getAttribute("aria-controls")}`
);
article.removeAttribute("hidden");
}
isahann commented
For anyone using Angular, I'm using the following solution:
- On the component class, create an array using these fields, each entry representing a tab and a component which will be rendered in the
<article>
section:
windowTabs: WindowTab[] = [{
id: 'tab-banana',
title: 'Banana',
selected: true,
component: BananaComponent
}, {
id: 'tab-apple',
title: 'Apple',
selected: false,
component: AppleComponent
},
// ...
];
Here's the interface WindowTab
used for typing:
export interface WindowTab {
id: string,
title: string,
selected: boolean,
component: any
}
- Create this function to switch tabs:
changeSelectedTab(selectedTab: WindowTab): void {
if (selectedTab.selected)
return;
this.windowTabs.forEach(tab => {
tab.selected = selectedTab.id === tab.id
})
}
- On your component HTML, inside the window, we can iterate on the WindowTab array:
<div class="window-body">
<section class="tabs">
<menu role="tablist" aria-label="Sample tabs">
<ng-container *ngFor="let tab of windowTabs">
<button
role="tab"
[attr.aria-controls]="tab.id"
[attr.aria-selected]="tab.selected"
(click)="this.changeSelectedTab(tab)"
>
{{tab.title}}
</button>
</ng-container>
</menu>
<ng-container *ngFor="let tab of windowTabs">
<article
role="tabpanel"
[id]="tab.id"
[hidden]="!tab.selected"
>
<ng-template [ngComponentOutlet]="tab.component"></ng-template>
</article>
</ng-container>
</section>
</div>
This will dynamically create the tabs, assign the function to switch to a tab, and also render its component when it gets clicked. Hope it helps anyone.