Tables

Course 2 · Ch 2
Tables in Depth
colspan/rowspan in real layouts, and the full accessibility picture beyond Fundamentals Chapter 6's basics

Fundamentals Chapter 6 introduced scope, caption, and basic spanning. This chapter goes further — building a genuinely complex, multi-level header table, and covering the accessibility attributes that matter once a table's structure is no longer simple rows and columns.

A Complex Header Structure with colspan/rowspan

<table> <tr> <th rowspan="2">Product</th> <th colspan="2">2026 Sales</th> </tr> <tr> <th>Q1</th> <th>Q2</th> </tr> <tr> <td>Widget</td> <td>£420</td> <td>£510</td> </tr> </table>
Product2026 Sales
Q1Q2
Widget£420£510

"Product" spans two rows since it has no separate sub-category; "2026 Sales" spans two columns since it's a grouping header for Q1 and Q2 beneath it. Working out colspan/rowspan values is genuinely easier by sketching the grid on paper first — getting them wrong silently shifts every following cell out of alignment.

A single miscounted colspan/rowspan silently misaligns the entire rest of the table
Unlike most HTML mistakes, a wrong span value doesn't produce an error — it just shifts subsequent cells, sometimes subtly, sometimes dramatically. Counting carefully (or building from a sketched grid) before writing the markup avoids a genuinely tedious class of bug to debug after the fact.

headers Attribute — Explicit Cell-to-Header Association

Fundamentals Chapter 6's scope handles simple row/column relationships well. For a genuinely complex table — like the multi-level header above — a data cell can explicitly reference which specific header IDs describe it, for the most precise possible accessibility association.

<th id="product">Product</th> <th id="q1">Q1</th> // ... <td headers="product q1">£420</td>

Reserve headers for cases where scope alone genuinely can't express the relationship — a cell that depends on headers from both a row group AND a column group simultaneously, exactly the multi-level case above.

Sortable Table Headers — A Common Real-World Pattern

Many real tables let a visitor click a header to sort by that column — purely a JavaScript behaviour, but the underlying markup convention is worth knowing.

<th> <button type="button">Name <span></span></button> </th>

Using a genuine <button> inside the header cell, rather than attaching a click handler directly to the <th>, keeps the element keyboard-accessible and properly announced as interactive — exactly the kind of distinction the browser security/accessibility courses return to repeatedly.

Responsive Tables — A Brief Practical Note

Wide tables on narrow (mobile) screens are a genuinely hard layout problem, solved primarily with CSS rather than HTML markup changes — wrapping the table in a horizontally scrollable container is the most common, robust approach:

<div style="overflow-x: auto;"> <table>...</table> </div>

Full coverage of responsive layout techniques belongs in the CSS courses — this is included here purely because it's such a common real-world pairing with genuinely wide tables.

Chapter 2 Quick Reference

  • colspan/rowspan in multi-level headers — sketch the grid first to avoid silent misalignment
  • headers="id1 id2" on a <td> — explicit header association for cases scope alone can't express
  • Sortable headers: wrap the clickable label in a real <button>, not a click handler on <th> directly
  • Wide tables on mobile — wrap in a horizontally scrollable container; full responsive technique belongs in CSS
  • Next chapter: multimedia in depth — video/audio attributes, tracks/subtitles, the picture element for responsive images