Lernen: Tutorial

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 Inhaltsbereich
  • aria-controls ist eine Alternative zur Identifikation des Ziel-Elements
  • aria-expanded zeigt den aktuellen Status (offen/geschlossen) an
  • data-bs-parent definiert die Akkordeon-Gruppe
  • collapsed markiert den Button als geschlossen

  • Schritt 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:

    1. Prüfe, ob das angeklickte Element bereits offen ist
    2. Schließe alle anderen Items in der gleichen Akkordeon-Gruppe
    3. Ö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?

    1. Element wird sichtbar gemacht (display: block), aber mit Höhe 0
    2. Die tatsächliche Höhe wird mit scrollHeight gemessen
    3. requestAnimationFrame sorgt dafür, dass der Browser die Änderung rendert
    4. Die Höhe wird auf den gemessenen Wert gesetzt - das triggert die CSS-Transition
    5. 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

    1. HTML-Struktur mit semantischen Klassen und ARIA-Attributen für Barrierefreiheit
    2. Event-Listener auf allen Akkordeon-Buttons registrieren
    3. Target-Elemente über data-Attribute identifizieren
    4. Gruppen-Logik sorgt dafür, dass nur ein Item geöffnet ist
    5. Animationen nutzen scrollHeight und requestAnimationFrame für flüssige Übergänge
    6. 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.

    Anschauen und Quelltext holen