uHTML.de Nachschlagewerke für die tägliche HTML-Arbeit

Tabellen

Zuletzt geprüft am von Daniel Weihmann

Tabellen sind für Daten da, nicht für Layout. Das ist die eine Regel, die seit zwanzig Jahren gilt und trotzdem wiederholt werden muss. Alles, was mit Anordnung zu tun hat, macht heute CSS Grid oder Flexbox. Was in eine Tabelle gehört, erkennt man daran, dass jede Zelle über zwei Angaben bestimmt ist: ihre Zeile und ihre Spalte.

Der Grund ist nicht Rechthaberei, sondern die Ausgabe: Ein Screenreader liest eine Tabelle anders vor als Fließtext. Er kündigt Größe und Kopfzeilen an und nennt bei jeder Zelle, zu welcher Spalte und Zeile sie gehört. Steckt darin ein Seitenlayout, ist das Ergebnis unbrauchbar.

Der Aufbau

<table>
  <caption>Öffnungszeiten der Filialen</caption>
  <thead>
    <tr>
      <th scope="col">Filiale</th>
      <th scope="col">Werktags</th>
      <th scope="col">Samstag</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">Köthen</th>
      <td>9 bis 18 Uhr</td>
      <td>9 bis 13 Uhr</td>
    </tr>
  </tbody>
</table>
<caption>
Der Name der Tabelle. Muss als erstes Kind direkt nach <table> stehen. Screenreader lesen ihn vor der Tabelle vor, damit klar ist, was da gleich kommt. Soll er nicht sichtbar sein, gehört er trotzdem ins Markup und wird per CSS ausgeblendet – aber mit einer Technik, die ihn für Hilfsmittel erhält, nicht mit display: none.
<thead>, <tbody>, <tfoot>
Gliedern die Tabelle in Kopf, Rumpf und Fuß. Beim Drucken langer Tabellen wiederholen Browser den <thead> auf jeder Seite. Ein <tfoot> nimmt Summenzeilen auf und darf im Quelltext heute an seiner logischen Stelle am Ende stehen.
<th> gegen <td>
<th> ist eine Kopfzelle, <td> eine Datenzelle. Kopfzellen stehen nicht nur oben: Die erste Spalte einer Zeile ist oft ebenfalls eine, siehe „Köthen" im Beispiel.
scope
Sagt, wofür eine Kopfzelle zuständig ist – col für die Spalte darunter, row für die Zeile daneben. Das ist die wichtigste einzelne Angabe in einer Tabelle: Ohne sie muss der Screenreader raten, und aus „Köthen, 9 bis 18 Uhr" wird eine Zahlenkette ohne Bezug.

Zellen verbinden

colspan zieht eine Zelle über mehrere Spalten, rowspan über mehrere Zeilen. Beide erwarten eine Anzahl, nicht eine Zielposition:

<tr>
  <th scope="row" rowspan="2">Köthen</th>
  <td>Vormittag</td>
</tr>
<tr>
  <td>Nachmittag</td>
</tr>

In der zweiten Zeile fehlt die erste Zelle bewusst, sie ist von oben belegt. Verrechnet man sich dabei, verschiebt sich der Rest der Tabelle – ein Fehler, den der Validator zuverlässig meldet.

Bei Tabellen mit zwei Kopfebenen reicht scope nicht mehr. Dann bekommen die Kopfzellen eine id, und die Datenzellen verweisen mit headers darauf. Das ist aufwendig – und meistens ein Hinweis darauf, dass zwei einfache Tabellen die bessere Lösung wären.

Tabellen auf schmalen Bildschirmen

Eine Tabelle lässt sich nicht sinnvoll umbrechen: Ihre Spalten hängen zusammen. Der Weg, der am wenigsten kaputtmacht, ist deshalb, sie in einem eigenen Bereich waagerecht scrollen zu lassen, statt die ganze Seite zu sprengen.

<div class="table-scroll" tabindex="0" role="region"
     aria-label="Öffnungszeiten">
  <table> … </table>
</div>

Das tabindex="0" ist kein Schmuck: Ohne es lässt sich ein scrollbarer Bereich nicht mit der Tastatur erreichen, wer also keine Maus benutzt, kommt an die rechten Spalten nicht heran. Zusammen mit role="region" und einem Namen wird daraus ein Bereich, den Screenreader ansteuern können.

Achtung bei Grid und Flexbox: Steht eine Tabelle in einer Grid-Spalte mit 1fr, zieht sie die Spalte auf ihre eigene Mindestbreite auf und sprengt das Layout trotz overflow-x: auto. Die Spalte braucht dann minmax(0, 1fr), damit der Scroll-Container greifen kann.

Darstellung

Zwei Kleinigkeiten machen Tabellen deutlich lesbarer. Zum einen border-collapse: collapse, damit aus doppelten Rahmen einfache Linien werden. Zum anderen die Ausrichtung: Text steht links, Zahlen rechts, und zwar Kopf- wie Datenzelle. So stehen Einer unter Einern und Zehner unter Zehnern, was den Vergleich überhaupt erst möglich macht. Wer mit font-variant-numeric: tabular-nums arbeitet, bekommt zusätzlich gleich breite Ziffern.

Für die Breite einzelner Spalten gibt es <colgroup> und <col>. Damit lässt sich eine Spalte gezielt ansprechen, ohne jeder Zelle eine Klasse zu geben – nützlich, wenn die Tabelle aus einer Datenquelle erzeugt wird.

Häufige Fehler

  • Kopfzeile aus <td> mit fetter Schrift. Sieht gleich aus, ist aber keine Kopfzeile. Für Hilfsmittel existiert der Bezug dann nicht.
  • scope vergessen. Der häufigste Befund in Prüfberichten zu Tabellen.
  • Leere Zellen für Abstand. Werden mitvorgelesen. Abstand macht padding.
  • Verschachtelte Tabellen. Erlaubt, aber beim Vorlesen kaum zu durchdringen. Fast immer lässt sich das auflösen.
  • Feste Pixelbreiten. Eine Tabelle mit width="900" passt auf kein Telefon. Prozentwerte oder gar keine Angabe plus Scroll-Container sind der bessere Weg.
  • Die Tabelle ist eigentlich eine Liste. Eine einspaltige Tabelle ist ein <ul>. Zwei Spalten aus Begriff und Erklärung sind eine Definitionsliste.