Skip to content

Building a tabbed component

Tabs (Tabbys) zeigen und verbergen Inhalte – wie Registerkarten im Browser, nur innerhalb der Seite.

Ein Tabby besteht aus zwei Komponenten:

  1. aus den Tabs
  2. aus den Inhalten der jeweiligen Tabs

Wir starten mit folgendem HTML:

<div class="tabby">
<div class="tabs">
<button class="tab">Tab 1</button>
<button class="tab">Tab 2</button>
<button class="tab">Tab 3</button>
</div>
<div class="tabs-content">
<section class="tab-content">Content section 1</section>
<section class="tab-content">Content section 2</section>
<section class="tab-content">Content section 3</section>
</div>
</div>

Da ein Tab (und sein zugehöriger Inhalt) immer geöffnet sein müssen, vergeben wir gleich die Klasse is-selected an den ersten Tab und den ersten Inhalt:

<div class="tabby">
<div class="tabs">
<button class="tab is-selected">Tab 1</button>
<button class="tab">Tab 2</button>
<button class="tab">Tab 3</button>
</div>
<div class="tabs-content">
<section class="tab-content is-selected">Content section 1</section>
<section class="tab-content">Content section 2</section>
<section class="tab-content">Content section 3</section>
</div>
</div>

Ein geöffneter Tab muss sich optisch deutlich von den geschlossenen Tabs abheben. Dafür verwenden wir hier filter:

// closed tabs are de-emphasized
.tab {
filter: grayscale(75%) brightness(0.9);
}
// emphasize the selected tab
.tab.is-selected {
filter: none;
background-color: white;
}

Wenn ein Tab ausgewählt ist, soll der zugehörige Inhalt angezeigt und die anderen Tabs versteckt werden. Wir machen das mit display: none.

// hide tab-content when not selected
.tab-content {
display: none;
}
// show selected tab-content
.tab-content.is-selected {
display: block;
}

Wenn ein Tab ausgewählt wird, müssen folgende Dinge passieren:

  1. die Klasse is-selected muss vom aktuell angezeigten Tab entfernt werden
  2. die Klasse is-selected muss vom aktuell angezeigten Tab-Content entfernt werden
  3. die Klasse is-selected muss dem ausgewählten Tab zugewiesen werden
  4. die Klasse is-selected muss dem Inhalt des ausgewählten Tabs zugewiesen werden
<div class="tabby">
<div class="tabs">
<button class="tab">Tab 1</button>
<button class="tab is-selected">Tab 2</button>
<button class="tab">Tab 3</button>
</div>
<div class="tabs-content">
<section class="tab-content">Content section 1</section>
<section class="tab-content is-selected">Content section 2</section>
<section class="tab-content">Content section 3</section>
</div>
</div>

Wenn der User einen Tab anklickt, muss dieser ausgewählt werden. Folgende Schritte müssen wir mit JavaScript machen:

  1. einen EventListener für die Tabs hinzufügen
  2. den geklickten Tab finden
  3. den zugehörigen Tab-Content identifizieren
  4. is-selected von allen Tabs entfernen
  5. is-selected von allen Tab-Inhalten entfernen
  6. is-selected dem angeklickten Tab zuweisen
  7. is-selected dem zugehörigen Inhalt zuweisen

Wir verwenden querySelectorAll, um alle Tabs auszuwählen. Dann loopen wir mit forEach durch die einzelnen Tabs und verwenden addEventListener, um einen EventListener für jeden Tab hinzuzufügen.

const tabs = Array.from(document.querySelectorAll('.tab'));
tabs.forEach(tab => {
tab.addEventListener('click', e => {
// do sth.
});
});

Wenn ein Event feuert, feuert es auf dem Tab, der geklickt wurde. Das können wir loggen:

tabs.forEach(tab => {
tab.addEventListener('click', e => {
console.log(tab);
});
});

Nun müssen wir noch den zugehörigen Tab-Content identifizieren. Dafür gibt es zwei Möglichkeiten:

  • Elemente zählen
  • Ein Zielelement festlegen

Für Tabby macht ein Zielelement mehr Sinn, weil jedes Tab seinen ihm zugeordneten Tab-Content hat (wie man zählt, schauen wir uns im Carousel an).

Um ein Zielelement festzulegen, machen wir zwei Dinge:

  1. Wir geben dem Tab-Content eine id mit einem bestimmten Wert
  2. Wir geben dem Tab ein Attribut (z.B. data-target), das denselben (einzigartigen) Wert enthält wie die id des zugehörigen Inhalts.
    Das funktioniert, weil es keine zwei Elemente mit derselben id geben darf.
<div class="tabby">
<div class="tabs">
<button class="tab is-selected" data-target="content-1">Tab 1</button>
<button class="tab" data-target="content-2">Tab 2</button>
<button class="tab" data-target="content-3">Tab 3</button>
</div>
<div class="tabs-content">
<section id="content-1" class="tab-content is-selected">Content section 1</section>
<section id="content-2" class="tab-content">Content section 2</section>
<section id="content-3" class="tab-content">Content section 3</section>
</div>
</div>

Damit können wir herauslesen, welches Tab der User geklickt hat …

tabs.forEach(tab => {
tab.addEventListener('click', e => {
const target = tab.dataset.target;
console.log(target);
});
});

… und in weiterer Folge bestimmen, welcher Tab-Content dazugehört:

tabs.forEach(tab => {
tab.addEventListener('click', e => {
const target = tab.dataset.target;
const tabContent = tabby.querySelector('#' + target);
console.log(tabContent);
});
});

Um den geklickten Tab nun sichtbar zu machen, müssen wir nur noch die Klassen richtig setzen.

  1. is-selected vom aktuell aktiven Tab entfernen (wir loopen einfach über alle Tabs)
  2. is-selected dem angeklickten Tab zuweisen
tabs.forEach(tab => {
tab.addEventListener('click', e => {
// ...
tabs.forEach(t => t.classList.remove('is-selected'));
tab.classList.add('is-selected');
});
});

Wir nutzen hier eine neue Variable t, weil wir tab nicht überschreiben wollen.

Um den zugehörigen Tab-Content auszuwählen, verwenden wir denselben Ansatz, also über alle Tab-Contents drüberloopen und die is-selected Klasse entfernen und dem ausgewählten Tab-Content wieder hinzufügen:

const tabContents = Array.from(tabby.querySelectorAll('.tab-content'));
tabs.forEach(tab => {
tab.addEventListener('click', e => {
// ...
tabContents.forEach(c => c.classList.remove('is-selected'));
tabContent.classList.add('is-selected');
});
});