💯 HTML lernen – kostenlos, werbefrei im Inhalt, ohne Anmeldung 🍃 Keine Cookies · 👁️ Kein Tracking · 🔓 Keine Anmeldung

Komplexe, zugängliche Tabellen

Das lernst du in dieser Lektion

  • Du weißt, warum jede Datentabelle eine caption braucht und was sie leisten muss.
  • Du setzt scope sicher ein und erkennst, ab wann es nicht mehr ausreicht.
  • Du beherrschst die headers/id-Technik für mehrstufige Kopfzeilen.
  • Du nutzt colspan, rowspan, colgroup und tfoot gezielt und sparsam.
  • Du machst breite Tabellen mit einem barrierefreien Scroll-Container mobiltauglich.

Für Sehende erklärt sich eine Tabelle durch einen Blick – für alle anderen durch ihre Struktur. Wer eine Tabelle sieht, erfasst Zeilen, Spalten und Zusammenhänge in Sekundenbruchteilen. Ein Screenreader dagegen liest Zelle für Zelle vor, und ob dabei „März, Umsatz, 12.400 Euro“ herauskommt oder nur ein zusammenhangloses „12.400“, entscheidet allein dein Markup. In dieser Lektion baust du Tabellen, die beide Welten bedienen: mehrstufige Kopfzeilen, verbundene Zellen, Summenzeilen – und eine responsive Strategie, die auch auf dem Smartphone niemanden aussperrt.

Grundgerüst und caption: das Pflichtprogramm

Zur Erinnerung, kompakt in drei Sätzen: Eine Tabelle lebt in <table>, jede Zeile ist ein <tr>, Datenzellen sind <td> und Kopfzellen <th>. Die Bereiche <thead>, <tbody> und <tfoot> gliedern Kopf, Rumpf und Fuß. Und die eiserne Grundregel gilt weiterhin: Tabellen sind ausschließlich für tabellarische Daten da – nie für Layout.

Zum Pflichtprogramm jeder Datentabelle gehört von nun an ein Element, das in den Grundlagen oft übergangen wird: <caption>. Es steht als erstes Kind direkt in der <table> und benennt, was die Tabelle zeigt. Für Screenreader-Nutzerinnen ist es der entscheidende Kontext: Viele lassen sich zunächst alle Tabellen einer Seite auflisten – und entscheiden anhand der Caption, ob sich das zellenweise Durcharbeiten lohnt. Eine Tabelle ohne Caption ist für sie ein unbeschriftetes Paket. Aber auch sehende Nutzer profitieren, denn eine gute Caption macht die Tabelle unabhängig vom umgebenden Text verständlich.

Formuliere die Caption präzise statt generisch: „Quartalsumsätze 2026 nach Filiale, in Euro“ schlägt „Übersicht“ um Längen. Wenn dein Design keine sichtbare Überschrift vorsieht, blende die Caption per CSS visuell aus (mit einer sogenannten „visually hidden“-Technik) – aber lass sie im Markup stehen. Weglassen ist keine Option.

scope vertieft: col, row, colgroup, rowgroup

Ein <th> sagt nur: „Ich bin eine Kopfzelle.“ Wofür sie Kopf ist – für die Spalte darunter oder die Zeile daneben –, muss der Screenreader sonst raten. Das scope-Attribut beendet das Raten:

  • scope="col" – diese Kopfzelle gilt für ihre Spalte (der Normalfall in der Kopfzeile).
  • scope="row" – diese Kopfzelle gilt für ihre Zeile (typisch für die erste Zelle jeder Datenzeile).
  • scope="colgroup" / scope="rowgroup" – die Kopfzelle überspannt eine ganze Spalten- bzw. Zeilengruppe, etwa wenn eine übergeordnete Zelle per colspan mehrere Spalten zusammenfasst.

Der Gewinn ist unmittelbar hörbar: Navigiert eine Nutzerin zu einer Zelle, liest der Screenreader die zugehörigen Kopfzellen mit vor – „Filiale Aschaffenburg, März: 12.400“. Ohne scope funktioniert das in einfachen Tabellen oft noch per Heuristik, in komplexeren wird es zuverlässig falsch.

Einfache Tabelle mit caption und beiden scope-Richtungen
<table>
  <caption>Öffnungszeiten der Stadtbücherei Stockstadt</caption>
  <thead>
    <tr>
      <th scope="col">Wochentag</th>
      <th scope="col">Vormittag</th>
      <th scope="col">Nachmittag</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">Dienstag</th>
      <td>10–12 Uhr</td>
      <td>15–18 Uhr</td>
    </tr>
    <tr>
      <th scope="row">Donnerstag</th>
      <td>geschlossen</td>
      <td>15–19 Uhr</td>
    </tr>
  </tbody>
</table>

Bis hierher trägt scope problemlos. An seine Grenze kommt es, wenn eine Datenzelle Kopfzellen zugeordnet werden muss, die nicht sauber in ihrer eigenen Spalte oder Zeile stehen – typischerweise bei zwei oder mehr übereinanderliegenden Kopfzeilen-Ebenen, bei denen sich Zuordnungen überkreuzen, oder wenn mitten in der Tabelle Zwischenüberschriften auftauchen. Dann braucht es schwereres Gerät.

headers und id: die Technik für mehrstufige Kopfzeilen

Die headers/id-Technik stellt die Zuordnung explizit her: Jede Kopfzelle bekommt eine eindeutige id, und jede Datenzelle listet in ihrem headers-Attribut die IDs aller Kopfzellen auf, die sie beschreiben – durch Leerzeichen getrennt, von der allgemeinsten zur speziellsten. Das ist Schreibarbeit, aber es ist die einzige Technik, die bei beliebig verschachtelten Köpfen zuverlässig funktioniert. Ein Quartalsbericht mit zwei Kopfebenen zeigt das Muster vollständig:

Quartalsbericht mit zwei Kopfebenen – headers/id komplett
<table>
  <caption>Umsatz und Kosten je Filiale, 1. Halbjahr 2026 (in Tausend Euro)</caption>
  <thead>
    <tr>
      <td rowspan="2"></td>
      <th id="q1" colspan="2" scope="colgroup">1. Quartal</th>
      <th id="q2" colspan="2" scope="colgroup">2. Quartal</th>
    </tr>
    <tr>
      <th id="q1-umsatz" headers="q1" scope="col">Umsatz</th>
      <th id="q1-kosten" headers="q1" scope="col">Kosten</th>
      <th id="q2-umsatz" headers="q2" scope="col">Umsatz</th>
      <th id="q2-kosten" headers="q2" scope="col">Kosten</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th id="f-ab" scope="row">Aschaffenburg</th>
      <td headers="q1 q1-umsatz f-ab">210</td>
      <td headers="q1 q1-kosten f-ab">148</td>
      <td headers="q2 q2-umsatz f-ab">242</td>
      <td headers="q2 q2-kosten f-ab">155</td>
    </tr>
    <tr>
      <th id="f-st" scope="row">Stockstadt</th>
      <td headers="q1 q1-umsatz f-st">95</td>
      <td headers="q1 q1-kosten f-st">61</td>
      <td headers="q2 q2-umsatz f-st">118</td>
      <td headers="q2 q2-kosten f-st">64</td>
    </tr>
  </tbody>
</table>

Lies eine beliebige Datenzelle und vollziehe die Kette nach: Die 242 gehört zum 2. Quartal, dort zur Spalte Umsatz, und zur Zeile Aschaffenburg – genau das liest ein Screenreader jetzt vor. Beachte auch die leere Ecke oben links: Sie ist bewusst ein <td> und kein <th>, denn sie ist Kopf von gar nichts.

colspan und rowspan: mächtig, aber sparsam

Mit colspan überspannt eine Zelle mehrere Spalten, mit rowspan mehrere Zeilen – im Quartalsbeispiel oben hast du beides schon im Einsatz gesehen. Zwei Handwerksregeln dazu: Erstens muss die Zellen-Arithmetik aufgehen – jede Zeile muss rechnerisch auf dieselbe Spaltenzahl kommen, sonst rendert der Browser ein schiefes Gitter, ohne sich zu beschweren. Zweitens: Minimiere verbundene Zellen. Jede Verbindung macht die Tabelle schwerer vorlesbar, schwerer per Tastatur navigierbar, schwerer stylbar und schwerer wartbar. Verbundene Zellen im Tabellenkopf sind meist legitim (wie die Quartals-Überschriften); verbundene Zellen im Datenrumpf sind fast immer ein Zeichen, dass eigentlich zwei Tabellen oder eine andere Struktur gemeint waren.

colgroup, tfoot und die Sache mit den Zahlen

Drei Werkzeuge runden den Werkzeugkasten ab. Mit <colgroup> und <col> deklarierst du direkt nach der Caption die Spalten der Tabelle – und kannst ganze Spalten stylen, ohne in jeder Zeile Klassen zu wiederholen: eine Klasse am <col> statt dreißigmal am <td>. (Aus technischen Gründen wirken auf <col> nur wenige CSS-Eigenschaften, vor allem Hintergrund, Rahmen und Breite – für das klassische „Spalte hervorheben“ reicht das völlig.)

Das <tfoot>-Element nimmt Summen- und Ergebniszeilen auf. Der semantische Gewinn: Programme wissen, dass hier keine weiteren Datensätze stehen, sondern eine Zusammenfassung – und beim Druck langer Tabellen kann der Browser Kopf und Fuß auf jeder Seite wiederholen.

Und die Zahlen selbst? Dass Beträge rechtsbündig und am Dezimaltrennzeichen ausgerichtet gehören, ist Aufgabe von CSS (text-align: right und tabellarische Ziffern per font-variant-numeric), nicht von HTML – aber deine sauber deklarierten Spalten machen genau dieses Styling trivial.

colgroup für Spalten-Styling und tfoot für die Summenzeile
<table>
  <caption>Vereinskasse 2026: Einnahmen je Sparte, in Euro</caption>
  <colgroup>
    <col>
    <col class="zahlen" span="2">
  </colgroup>
  <thead>
    <tr>
      <th scope="col">Sparte</th>
      <th scope="col">Beiträge</th>
      <th scope="col">Spenden</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">Fußball</th>
      <td>4.200</td>
      <td>860</td>
    </tr>
    <tr>
      <th scope="row">Turnen</th>
      <td>2.950</td>
      <td>410</td>
    </tr>
  </tbody>
  <tfoot>
    <tr>
      <th scope="row">Summe</th>
      <td>7.150</td>
      <td>1.270</td>
    </tr>
  </tfoot>
</table>

Wann eine Tabelle das falsche Werkzeug ist

Nicht alles, was nach Tabelle aussieht, ist eine. Der klassische Fehlgriff: Schlüssel-Wert-Paare wie ein Steckbrief („Name: …, Gründungsjahr: …, Mitglieder: …“) in eine zweispaltige Tabelle zu pressen. Hier gibt es keine Datensätze, die sich über Zeilen und Spalten vergleichen ließen – es gibt nur Begriffe und ihre Beschreibungen. Genau dafür existiert die Beschreibungsliste: <dl> mit <dt> (Begriff) und <dd> (Beschreibung). Sie ist leichter, responsiv ohne Tricks und semantisch ehrlich. Die Testfrage lautet: Würde eine zweite Datenzeile mit denselben Spalten Sinn ergeben? Wenn nein, ist es keine Tabelle.

Responsive Strategie: der barrierefreie Scroll-Container

Breite Tabellen und schmale Smartphone-Displays vertragen sich nicht – und Tabellen umbrechen wie Fließtext können Browser nicht. Die robusteste Strategie: Die Tabelle behält ihre natürliche Breite und wird in einen horizontal scrollbaren Container gelegt. Damit dieser Container barrierefrei wird, braucht er drei Zutaten: tabindex="0", damit Tastatur-Nutzer ihn fokussieren und mit den Pfeiltasten scrollen können (sonst wäre der abgeschnittene Inhalt für sie unerreichbar); role="region", damit assistive Technologien ihn als benannten Seitenbereich behandeln; und ein aria-label, das dem fokussierten Bereich einen Namen gibt. Fehlt eine der drei Zutaten, ist der Scroll-Container nur für Maus- und Touch-Nutzer gebaut.

Probier es ausÄndere den Code – die Vorschau aktualisiert sich sofort.

Zusammenfassung

In der nächsten Lektion zeigt HTML seine vielleicht unterschätzteste Seite: Interaktion ohne eine einzige Zeile JavaScript. Aufklappbare Bereiche, Dialoge, Popovers und Fortschrittsanzeigen – der Browser hat all das längst eingebaut.

Teste dein Wissen

5 kurze Fragen zu dieser Lektion – die Auswertung passiert direkt im Browser.

1 Warum ist <caption> für Screenreader-Nutzer so wichtig?
2 Wann reicht scope nicht mehr aus und du brauchst die headers/id-Technik?
3 Ein Steckbrief mit Paaren wie „Gründungsjahr: 1921“ soll ins HTML. Welches Element ist richtig?
4 Welche drei Zutaten machen einen Tabellen-Scroll-Container barrierefrei?
5 Warum solltest du colspan und rowspan im Datenbereich möglichst vermeiden?

Weiter: Interaktion ohne JavaScript →