Tab
Как сверстать табы.
Статья о доступности Aria: tab role
Пример использования
HTML
<section class="js-tabs base-tabs">
<div role="tablist" aria-label="Sample Tabs">
<button
role="tab"
aria-selected="true"
aria-controls="panel-1"
id="tab-1"
tabindex="0"
class="base-tabs__button"
>
First Tab
</button>
<button
role="tab"
aria-selected="false"
aria-controls="panel-2"
id="tab-2"
tabindex="-1"
class="base-tabs__button"
>
Second Tab
</button>
<button
role="tab"
aria-selected="false"
aria-controls="panel-3"
id="tab-3"
tabindex="-1"
class="base-tabs__button"
>
Third Tab
</button>
</div>
<div id="panel-1" role="tabpanel" tabindex="0" aria-labelledby="tab-1">
<p>Content for the first panel</p>
</div>
<div id="panel-2" role="tabpanel" tabindex="0" aria-labelledby="tab-2" hidden>
<p>Content for the second panel</p>
</div>
<div id="panel-3" role="tabpanel" tabindex="0" aria-labelledby="tab-3" hidden>
<p>Content for the third panel</p>
</div>
</section>
TS
const TABS_SELECTOR = ".js-tabs";
const TABLIST_SELECTOR = '[role="tablist"]';
const TABPANEL_SELECTOR = '[role="tabpanel"]';
const TAB_SELECTOR = '[role="tab"]';
const tabsElements = document.querySelectorAll<HTMLElement>(TABS_SELECTOR);
function initTabs(element: HTMLElement) {
const tabList = element.querySelector<HTMLElement>(TABLIST_SELECTOR);
if (!tabList) return;
let tabFocus = 0;
const tabPanelElements =
element.querySelectorAll<HTMLElement>(TABPANEL_SELECTOR);
const tabs = tabList.querySelectorAll<HTMLButtonElement>(TAB_SELECTOR);
function handleTabClick(e: Event) {
const targetTab = e.target as HTMLButtonElement;
const activeButtonElements =
tabList?.querySelectorAll(':scope > [aria-selected="true"]') ?? [];
// Remove all current selected tabs
for (const button of activeButtonElements) {
button.setAttribute("aria-selected", "false");
}
// Set this tab as selected
targetTab.setAttribute("aria-selected", "true");
// Hide all tab panels
for (const tabPanelElement of tabPanelElements) {
tabPanelElement.setAttribute("hidden", "true");
}
// Show the selected panel
element
.querySelector(`#${targetTab.getAttribute("aria-controls")}`)
?.removeAttribute("hidden");
}
function changeTab(e: KeyboardEvent) {
// Move right
if (e.key === "ArrowRight" || e.key === "ArrowLeft") {
tabs[tabFocus].setAttribute("tabindex", "-1");
if (e.key === "ArrowRight") {
tabFocus++;
// If we're at the end, go to the start
if (tabFocus >= tabs.length) {
tabFocus = 0;
}
// Move left
} else {
tabFocus--;
// If we're at the start, move to the end
if (tabFocus < 0) {
tabFocus = tabs.length - 1;
}
}
tabs[tabFocus].setAttribute("tabindex", "0");
tabs[tabFocus].focus();
}
}
for (const tab of tabs) {
tab.addEventListener("click", handleTabClick);
}
tabList.addEventListener("keydown", changeTab);
}
for (const tabElement of tabsElements) {
initTabs(tabElement);
}
Стили
Стилизовать активную кнопку через атрибут [aria-selected="true"]
.base-tabs {
&__button {
background-color: #fff;
&[aria-selected="true"] {
background-color: blue;
}
}
}