call(back)
FrontendmediumSign in to star problems

Tabs: initTabs(node)

Create a tab UI that switches panels when the tabs are clicked, packaged as initTabs(node) — a jQuery-plugin-style initializer — so the two tab sets already on the page work independently. (Prefer React? Switch the template — the same exercise is there as a Tabs component rendered twice.)

Requirements

  • Exactly one panel per tab set is visible; clicking a tab shows its panel and moves the active styling.
  • The first tab is selected on load.
  • initTabs(root) wires up one [data-tabs] element and returns a small API (activate(index)). Initializing the second set must not touch the first.
  • Use "the right ID pattern": each tab's aria-controls points at its panel's id and each panel's aria-labelledby points back — unique across instances.
  • Keep aria-selected in sync; hide inactive panels with the hidden attribute.
  • Left/Right arrow keys move between tabs and wrap around, with a roving tabindex so only the active tab is in the tab order.

Hints

Hint 1

Scope every query to the root: root.querySelector(':scope > [role="tablist"]') and :scope > [role="tabpanel"] — that's what stops nested or sibling tab sets from hijacking each other.

Hint 2

An instance counter (tabs-1, tabs-2, …) gives each set unique ids for aria-controls / aria-labelledby; one delegated click listener on the tablist replaces one listener per tab. In React, useId() is that counter.

Hint 3

Write a single activate(index) that sets aria-selected, tabIndex, the active class, and hidden for every tab/panel pair — click, keyboard, and the initial state all call it.

Asked at