Newsletter

Tabs mit JavaScript und CSS bauen, inklusive ARIA-Attributen

Tabs mit JavaScript und CSS bauen, inklusive ARIA-Attributen

Dieses Tutorial baut funktionierende Tabs mit HTML, CSS und JavaScript. Die Idee ist schlicht: CSS- und JavaScript-Datei in die Seite einbinden, ein paar Klassen an verschachtelte divs vergeben, fertig. Nebenbei versuchen wir, das Markup semantisch sauber zu halten, und setzen ARIA-Attribute auf die passenden Elemente. Nimm diesen Teil aber nicht zu ernst, ich bin mir nicht sicher, ob ich ihn genau richtig hinbekommen habe. So oder so lohnt es sich immer, deinem HTML eine Bedeutung zu geben.

See the Pen
Onglets HTLM/CSS/JS
by Damien Flandrin (@dam62500)
on CodePen.0

Das HTML der Tabs

So kann das Markup aussehen. Ein div mit der Klasse tabs, darin zwei weitere divs, eines mit der Klasse tab-registers und eines mit der Klasse tab-bodies. Der erste Button in tab-register gehört zum ersten Element in tab-bodies. Einer der Buttons kann die Klasse active-tab bekommen und wird damit zum aktiven Tab.

HTML der Tabs

html
<div class="tabs">
    <div class="tab-registers">
        <button class="active-tab">Tab 1</button>
        <button>Tab 2</button>
        <button>Tab 2</button>
    </div>
    <div class="tab-bodies">
        <div style="display:block;">
            Contenu Tab 1
        </div>
        <div style="display:none;">
            Contenu Tab 2
        </div>
        <div style="display:none;">
            Contenu Tab 3
        </div>
    </div>
</div>

Das JavaScript der Tabs

Zuerst holen wir uns eine HTML-Collection aller Elemente mit der Klasse tabs und iterieren darüber, nachdem wir sie mit Array.from(list_like) in eine Liste verwandelt haben. Der Callback der Schleife bekommt das Element selbst und seinen Index. Diesen Index brauchen wir, um jede Tab-Gruppe eindeutig zu identifizieren.

JavaScript der Tabs

javascript
Array.from(document.querySelectorAll('.tabs')).forEach((tab_container, TabID) => {
    ...
})

Danach legen wir die entscheidenden Elemente in Variablen ab, nämlich tabs-registers und tab-bodies.

JavaScript der Tabs

javascript
const registers = tab_container.querySelector('.tab-registers');
const bodies = tab_container.querySelector('.tab-bodies');

Weiter geht es mit dem aktiven Register. Es kann undefined sein, dann fallen wir auf den ersten Button zurück. In beiden Fällen bekommt das Element die Klasse active-tab.

JavaScript der Tabs

javascript
let activeRegister = registers.querySelector('.active-tab');
activeRegister = activeRegister ? activeRegister : registers.children[0]
activeRegister.classList.add('active-tab')

Anschließend rufen wir changeBody() auf. Die Funktion tut genau das, was ihr Name sagt: Sie blendet alle Bodies aus, bis auf den zum aktuellen Register, also zum aktuellen Tab. Dieselbe Funktion rufen wir bei jedem Tab-Wechsel auf.

Dann iterieren wir über alle Elemente, die direkt im Register-Container liegen, also über die Buttons, die Tabs selbst. Auf jedem setzen wir das Attribut aria-controls, das angibt, welches Element davon gesteuert wird, und wir vergeben die passende id auf dem zugehörigen Body. Diese id setzt sich aus der id der Tab-Gruppe und dem Index des Tab-Buttons zusammen und ist damit eindeutig.

Danach hängen wir einen Event-Listener an den Button: Wir nehmen dem aktuellen Register die Klasse des aktiven Tabs weg, ersetzen den Inhalt der Variablen durch das neue aktuelle Register und geben ihm seinerseits diese Klasse. Zum Schluss rufen wir changeBody() auf.

JavaScript der Tabs

javascript
Array.from(registers.children).forEach((el, i) => {
  el.setAttribute('aria-controls', `${TabID}_${i}`)
  bodies.children[i]?.setAttribute('id', `${TabID}_${i}`)

el.addEventListener('click', (ev) => {
    let activeRegister = registers.querySelector('.active-tab');
    activeRegister.classList.remove('active-tab')
    activeRegister = el;
    activeRegister.classList.add('active-tab')
    changeBody(registers, bodies, activeRegister)
  })
})

Jetzt zu changeBody() selbst. Darin iterieren wir erneut über die Kind-Register und nehmen den Index jeder Iteration mit. Existiert an diesem Index ein Body, setzen wir seine display-Eigenschaft je nach aktuellem Register auf block oder none. Diese Prüfung gibt es, weil es mehr Register als Bodies geben kann. Außerdem setzen wir am Button das Attribut aria-expanded, das dem Nutzer sagt, ob das Panel, oder der Button, der zu einem Panel führt, aufgeklappt ist oder nicht.

JavaScript der Tabs

javascript
function changeBody() {
    Array.from(registers.children).forEach((el, i) => {
        if (bodies.children[i]) {
            bodies.children[i].style.display = el == activeRegister ? 'block' : 'none'
        }

el.setAttribute('aria-expanded', el == activeRegister ? 'true' : 'false')
    })
}

Das CSS der Tabs

CSS der Tabs

css
.tabs {
    font-family: 'Lucida Sans', sans-serif;
    font-size: 20px;
}

.tabs .tab-registers {
    display: flex;
    background-color: RGB(255, 255, 255);
}

.tabs button {
    padding: 0.5em;
    background-color: RGB(255, 255, 255);
    border: none;
    font: inherit;
}

.tabs .tab-registers button:hover {
    cursor: pointer;
}

.tabs .tab-bodies {
    padding: 0.5em;
    background-color: RGB(235, 235, 235);
    flex-grow: 1;
    overflow-y: auto;
}

.tabs button.active-tab {
    background-color: rgb(235, 235, 235);
}

CSSHTMLJavaScript

Damien Flandrin Webentwickler seit 2010, Gründer von Gekkode und Email Impact. Jeder Artikel wird vor der Veröffentlichung an einem echten Projekt getestet. Kontakt
Newsletter

Neue Tests, Tutorials und Projekte, per E-Mail.

Reproduzierbare Tests, versionierter Code, datierte Ergebnisse. Niemals Spam.