En clase y en casa hicimos ejercicios de tablas bastante complicados en algunos casos, y la combinación de celdas es de lo más costoso de dominar: tienes que ir observando cómo cambia la tabla entera cada vez que tocas una propiedad de colspan o rowspan, porque un solo número mal puesto descuadra el resto de filas sin que sea evidente por qué.

La tabla más simple posible

Toda tabla HTML se construye con tres etiquetas básicas: <table> para el conjunto, <tr> para cada fila (table row) y <td> para cada celda (table data):

<table>
  <tr>
    <td>Nombre</td>
    <td>Edad</td>
    <td>Ciudad</td>
  </tr>
  <tr>
    <td>Marta</td>
    <td>22</td>
    <td>Madrid</td>
  </tr>
</table>

El problema de esta versión es que no hay ninguna diferencia visual ni semántica entre la fila de títulos y las filas de datos — para el navegador, y para cualquier lector de pantalla, todas las celdas son iguales.

Celdas de cabecera con <th>

Para marcar qué celdas son cabecera (título de columna) se usa <th> (table header) en vez de <td>:

<table>
  <tr>
    <th>Nombre</th>
    <th>Edad</th>
    <th>Ciudad</th>
  </tr>
  <tr>
    <td>Marta</td>
    <td>22</td>
    <td>Madrid</td>
  </tr>
</table>

Los navegadores muestran <th> en negrita y centrado por defecto, pero lo importante no es el aspecto — es que ahora la tabla comunica correctamente su estructura, algo que también aprovechan los lectores de pantalla para anunciar “columna Nombre” antes de cada dato.

Título de la tabla con <caption>

<caption> pone un título o leyenda a toda la tabla. Solo se puede usar una vez, y va justo después de la etiqueta <table>:

<table>
  <caption>Ventas por secciones</caption>
  <tr>
    <th>Enero</th>
    <td>12000</td>
  </tr>
</table>

Las tres zonas de una tabla: thead, tbody, tfoot

Aquí es donde una tabla deja de ser una lista plana de filas y pasa a tener estructura real. Tres etiquetas diferencian las tres partes principales:

  • <thead>: agrupa las filas que forman la cabecera de la tabla.
  • <tbody>: agrupa el cuerpo, los datos en sí.
  • <tfoot>: agrupa el pie, normalmente totales o resúmenes.
<table border="1">
  <caption>Ventas por secciones</caption>
  <thead>
    <tr>
      <th>&nbsp;</th>
      <th>Hardware</th>
      <th>Software</th>
    </tr>
  </thead>
  <tfoot>
    <tr>
      <th>Total</th>
      <th>25000</th>
      <th>22000</th>
    </tr>
  </tfoot>
  <tbody>
    <tr>
      <th>Enero</th>
      <td>12000</td>
      <td>15000</td>
    </tr>
    <tr>
      <th>Febrero</th>
      <td>13000</td>
      <td>9000</td>
    </tr>
  </tbody>
</table>

Separar estas tres zonas no es solo cuestión de orden en el código — permite aplicar estilos distintos a cada parte con CSS (por ejemplo, un fondo diferente para la cabecera y el pie) sin tener que ir marcando fila por fila con clases sueltas.

Combinar celdas: colspan y rowspan

Diagrama de una tabla mostrando una celda con colspan=2 ocupando dos columnas y otra con rowspan=2 ocupando dos filas

Esta es la parte que de verdad cuesta interiorizar. Los atributos colspan y rowspan, en <td> o <th>, hacen que una celda ocupe más de una columna o más de una fila:

  • colspan="2": la celda ocupa el ancho de 2 columnas.
  • rowspan="3": la celda ocupa el alto de 3 filas.

Lo que hace que esto se complique en la práctica es que cada celda que “se come” otra celda por un colspan o rowspan, tienes que dejar de escribirla en las filas siguientes — si no, la tabla entera se descuadra, porque el navegador sigue esperando el número normal de celdas por fila salvo que le digas explícitamente que una celda ocupa más espacio del habitual. Por eso el consejo real, más allá de la teoría, es ir probando en el navegador cada vez que añades un colspan o rowspan, en vez de intentar calcular la tabla entera de memoria antes de comprobarla.

Por qué esto también importa para accesibilidad

Separar thead, tbody y tfoot, y usar th en vez de td para las cabeceras, no es solo una cuestión de organizar el código o de poder aplicar estilos distintos a cada zona. Un lector de pantalla, al llegar a una celda de datos, puede anunciar automáticamente a qué columna y fila de cabecera pertenece — “Enero, columna Hardware, 12000” — pero solo si esas cabeceras están marcadas correctamente con th y agrupadas en thead. Con una tabla hecha solo con td desde el principio hasta el final, esa información se pierde por completo, y una persona que dependa de un lector de pantalla no tiene forma de saber a qué corresponde cada número sin ir contando columnas de memoria.

Un error de otra época: usar tablas para maquetar

Antes de que CSS estuviera bien soportado por todos los navegadores, era habitual usar tablas HTML no para mostrar datos tabulares, sino para maquetar toda la página — meter el menú en una celda, el contenido en otra, el pie en una tercera fila, usando <table> como si fuera un sistema de columnas. Hoy esto está completamente desaconsejado: para maquetar se usan capas (div, elementos semánticos) junto con CSS (Flexbox o Grid), y las tablas se reservan exclusively para lo que su nombre indica — representar datos organizados en filas y columnas, como una lista de precios, un horario o una comparativa. Si te encuentras usando <table> para colocar cosas en la pantalla sin que haya datos tabulares de por medio, es una señal de que el problema que estás resolviendo es de maquetación, no de datos, y la herramienta correcta es otra.

Pon a prueba lo que has entendido

Añades `rowspan="2"` a una celda de la primera fila de una tabla. ¿Qué tienes que hacer en la fila siguiente para que la tabla no se descuadre?

Resumen rápido

Una tabla bien estructurada usa <th> para cabeceras, <caption> para el título, y separa <thead>, <tbody> y <tfoot> para diferenciar cabecera, cuerpo y pie. colspan y rowspan combinan celdas en horizontal y vertical respectivamente, pero exigen dejar de escribir las celdas “ocupadas” en las filas siguientes — es la parte que más merece la pena comprobar en el navegador en vez de calcular de memoria.