Tutorial: Interaktives Akkordeon mit JavaScript
Einleitung
Ein Akkordeon ist ein häufig verwendetes UI-Element, das es ermöglicht, viele Informationen platzsparend unterzubringen. Der Nutzer kann einzelne Bereiche auf- und zuklappen, um genau die Informationen zu sehen, die er gerade benötigt. In diesem Tutorial lernst du, wie du ein vollständig funktionsfähiges Akkordeon mit sanften Animationen selbst programmierst.
Die Problemstellung
Stell dir vor, du hast:
- Eine Webseite mit vielen Informationen
- Der Lesefluss soll nicht durch zu viel Text gestört werden
- Nutzer sollen selbst entscheiden, welche Details sie sehen möchten
- Die Übergänge sollen animiert und flüssig sein
- Es soll immer nur ein Element gleichzeitig geöffnet sein
Warum ist das wichtig?
Akkordeons helfen dabei:
- Komplexe Inhalte übersichtlich zu strukturieren
- Die Seite nicht mit zu viel Information zu überladen
- Dem Nutzer die Kontrolle über die Informationsdichte zu geben
- Mobile Ansichten benutzerfreundlicher zu gestalten
Die Herausforderung: Wir müssen JavaScript nutzen, um die Klick-Events zu verarbeiten, Höhen zu berechnen und sanfte Animationen zu ermöglichen.
Die Lösung: HTML + JavaScript
Wir bauen eine Akkordeon-Struktur in HTML und steuern das Verhalten komplett mit JavaScript.
Schritt 1: HTML-Struktur
Zunächst schauen wir uns die grundlegende HTML-Struktur an. Ein Akkordeon besteht aus mehreren Items, die jeweils einen Button (Header) und einen ausklappbaren Inhaltsbereich haben.
<div class="akkordeon" id="akkordeonbeispiel">
<div class="akkordeon-item">
<h2 class="akkordeon-header">
<button class="akkordeon-button collapsed"
type="button"
data-bs-toggle="collapse"
data-bs-target="#collapseOne"
aria-expanded="false"
aria-controls="collapseOne">
<strong>Erster Spoiler mit Zusatzinformationen</strong>
</button>
</h2>
<div id="collapseOne"
class="akkordeon-collapse collapse"
data-bs-parent="#akkordeonbeispiel">
<div class="akkordeon-body">
<p>Dein Inhalt hier...</p>
</div>
</div>
</div>
</div>
Wichtige HTML-Attribute:
data-bs-target verbindet den Button mit dem zugehörigen Inhaltsbereicharia-controls ist eine Alternative zur Identifikation des Ziel-Elementsaria-expanded zeigt den aktuellen Status (offen/geschlossen) andata-bs-parent definiert die Akkordeon-Gruppecollapsed markiert den Button als geschlossenSchritt 2: JavaScript - Event-Listener einrichten
Sobald die Seite geladen ist, suchen wir alle Akkordeon-Buttons und fügen Event-Listener hinzu.
document.addEventListener('DOMContentLoaded', function() {
// Alle Akkordeon-Buttons finden
const akkordeonButtons = document.querySelectorAll('.akkordeon-button');
akkordeonButtons.forEach(button => {
button.addEventListener('click', function() {
// Hier kommt die Logik für das Öffnen/Schließen
});
});
});
Was passiert hier? Wir warten, bis die Seite vollständig geladen ist (DOMContentLoaded), und registrieren dann für jeden Akkordeon-Button einen Klick-Handler.
Schritt 3: Target-Element finden
Wenn ein Button geklickt wird, müssen wir herausfinden, welches Element geöffnet oder geschlossen werden soll.
button.addEventListener('click', function() {
// Target-Element finden
const targetId = this.getAttribute('data-bs-target') ||
this.getAttribute('aria-controls');
const targetElement = document.querySelector(targetId ||
`#${this.getAttribute('aria-controls')}`);
if (!targetElement) return;
// Parent-Akkordeon finden
const akkordeonParent = this.closest('.akkordeon');
const akkordeonId = akkordeonParent ? akkordeonParent.id : null;
});
Wir lesen das data-bs-target Attribut aus (z.B. "#collapseOne") und finden damit das zugehörige div-Element. Außerdem identifizieren wir das übergeordnete Akkordeon, um später alle anderen Items in derselben Gruppe schließen zu können.
Schritt 4: Andere Items schließen
Ein klassisches Akkordeon-Verhalten ist, dass immer nur ein Item gleichzeitig geöffnet sein kann. Wenn du ein neues öffnest, schließen sich die anderen automatisch.
// Prüfen ob aktuelles Element bereits offen ist
const isCurrentlyOpen = !this.classList.contains('collapsed');
// Alle anderen Akkordeon-Items in derselben Gruppe schließen
if (akkordeonId) {
const allItemsInGroup = akkordeonParent.querySelectorAll('.akkordeon-item');
allItemsInGroup.forEach(item => {
const otherButton = item.querySelector('.akkordeon-button');
const otherCollapse = item.querySelector('.akkordeon-collapse');
if (otherButton !== this && otherCollapse) {
closeAkkordeonItem(otherButton, otherCollapse);
}
});
}
// Aktuelles Element öffnen/schließen
if (isCurrentlyOpen) {
closeAkkordeonItem(this, targetElement);
} else {
openAkkordeonItem(this, targetElement);
}
Die Logik:
- Prüfe, ob das angeklickte Element bereits offen ist
- Schließe alle anderen Items in der gleichen Akkordeon-Gruppe
- Öffne oder schließe das angeklickte Element
Schritt 5: Öffnen-Animation
Jetzt kommt der spannende Teil - die Animation. Wir berechnen die tatsächliche Höhe des Inhalts und animieren die Höhe von 0 auf den berechneten Wert.
function openAkkordeonItem(button, collapseElement) {
// Button-Status ändern
button.classList.remove('collapsed');
button.setAttribute('aria-expanded', 'true');
// Collapse-Element vorbereiten
collapseElement.style.display = 'block';
collapseElement.style.height = '0px';
collapseElement.classList.add('collapsing');
collapseElement.classList.remove('collapse');
// Höhe messen
const scrollHeight = collapseElement.scrollHeight;
// Animation starten
requestAnimationFrame(() => {
collapseElement.style.height = scrollHeight + 'px';
});
// Nach Animation abschließen
setTimeout(() => {
collapseElement.classList.remove('collapsing');
collapseElement.classList.add('collapse', 'show');
collapseElement.style.height = '';
}, 350);
}
Wie funktioniert die Animation?
- Element wird sichtbar gemacht (
display: block), aber mit Höhe 0 - Die tatsächliche Höhe wird mit
scrollHeightgemessen requestAnimationFramesorgt dafür, dass der Browser die Änderung rendert- Die Höhe wird auf den gemessenen Wert gesetzt - das triggert die CSS-Transition
- Nach 350ms (Animations-Dauer) wird aufgeräumt und die Höhe auf 'auto' gesetzt
Warum requestAnimationFrame?
Ohne requestAnimationFrame würde der Browser beide Höhen-Änderungen (0px und scrollHeight) gleichzeitig verarbeiten - es gäbe keine Animation. requestAnimationFrame zwingt den Browser, erst die Höhe 0 zu rendern, bevor die Animation startet.
Schritt 6: Schließen-Animation
Das Schließen funktioniert genau umgekehrt - von der vollen Höhe zurück auf 0.
function closeAkkordeonItem(button, collapseElement) {
// Button-Status ändern
button.classList.add('collapsed');
button.setAttribute('aria-expanded', 'false');
// Aktuelle Höhe setzen
collapseElement.style.height = collapseElement.scrollHeight + 'px';
collapseElement.classList.add('collapsing');
collapseElement.classList.remove('collapse', 'show');
// Animation starten
requestAnimationFrame(() => {
collapseElement.style.height = '0px';
});
// Nach Animation abschließen
setTimeout(() => {
collapseElement.style.display = 'none';
collapseElement.classList.remove('collapsing');
collapseElement.classList.add('collapse');
collapseElement.style.height = '';
}, 350);
}
Der Trick beim Schließen: Wir setzen zuerst die aktuelle Höhe explizit, damit der Browser einen Startwert für die Animation hat. Dann animieren wir auf 0px und verstecken das Element nach der Animation komplett.
Schritt 7: Initialisierung
Beim Laden der Seite sollen alle Akkordeon-Items geschlossen sein. Deshalb initialisieren wir alle Elemente entsprechend.
// Initial alle Akkordeon-Items als geschlossen markieren
akkordeonButtons.forEach(button => {
if (!button.classList.contains('collapsed')) {
button.classList.add('collapsed');
}
button.setAttribute('aria-expanded', 'false');
});
// Initial alle Collapse-Elemente verstecken
document.querySelectorAll('.akkordeon-collapse').forEach(collapse => {
if (!collapse.classList.contains('show')) {
collapse.style.display = 'none';
collapse.classList.add('collapse');
}
});
Bonus: Utility-Funktionen
Manchmal möchtest du Akkordeon-Items programmatisch öffnen oder schließen, z.B. über andere Buttons oder Events. Dafür gibt es Utility-Funktionen.
Ein bestimmtes Item öffnen
function openAkkordeon(targetId) {
const targetElement = document.querySelector(targetId);
const button = document.querySelector(
`[data-bs-target="${targetId}"],
[aria-controls="${targetId.replace('#', '')}"]`
);
if (targetElement && button) {
button.click();
}
}
// Verwendung:
openAkkordeon('#collapseTwo');
Alle Items in einer Gruppe schließen
function closeAllAkkordeons(akkordeonId) {
const akkordeon = document.querySelector(`#${akkordeonId}`);
if (akkordeon) {
const buttons = akkordeon.querySelectorAll(
'.akkordeon-button:not(.collapsed)'
);
buttons.forEach(button => button.click());
}
}
// Verwendung:
closeAllAkkordeons('akkordeonbeispiel');
Zusammenfassung
- HTML-Struktur mit semantischen Klassen und ARIA-Attributen für Barrierefreiheit
- Event-Listener auf allen Akkordeon-Buttons registrieren
- Target-Elemente über data-Attribute identifizieren
- Gruppen-Logik sorgt dafür, dass nur ein Item geöffnet ist
- Animationen nutzen scrollHeight und requestAnimationFrame für flüssige Übergänge
- Initialisierung stellt sicher, dass alle Items beim Start geschlossen sind
Wichtige Konzepte
scrollHeight: Diese Eigenschaft gibt die komplette Höhe des Inhalts zurück, auch wenn er aktuell nicht sichtbar ist. Das ist perfekt, um die Ziel-Höhe für unsere Animation zu ermitteln.
requestAnimationFrame: Synchronisiert Änderungen mit dem Rendering-Zyklus des Browsers. Das ist wichtig, damit Animationen flüssig laufen und der Browser die Änderungen in der richtigen Reihenfolge verarbeitet.
CSS-Klassen: Die Klassen collapsing, collapse und show werden dynamisch gesetzt. In deinem CSS kannst du diese nutzen, um Transitions zu definieren (z.B. transition: height 0.35s ease).
Mögliche Erweiterungen
Du kannst das Akkordeon noch erweitern: Erlaube mehrere gleichzeitig geöffnete Items (entferne die Gruppen-Logik), füge Icons hinzu, die sich beim Öffnen/Schließen drehen, speichere den Zustand im localStorage, damit nach einem Reload derselbe Status wiederhergestellt wird, oder füge Keyboard-Navigation hinzu (Pfeiltasten, Enter, Space).
Anwendungsfälle
Akkordeons eignen sich perfekt für FAQ-Bereiche, wo Nutzer gezielt nach Antworten suchen, Produktbeschreibungen mit technischen Details, die optional angezeigt werden, mobile Menüs mit Unterkategorien oder Content-Management-Systeme, wo viele Optionen übersichtlich dargestellt werden müssen.
Fazit
Ein Akkordeon ist ein mächtiges UI-Element, das Inhalte strukturiert und benutzerfreundlich zugänglich macht. Mit der Kombination aus semantischem HTML und intelligentem JavaScript schaffen wir ein interaktives Element mit sanften Animationen und zugänglichem Code. Der Schlüssel liegt in der Berechnung der Höhe und der geschickten Nutzung von requestAnimationFrame.