Tabelle HTML: come usarle, quando usarle

Utilizzare le tabelle HTML nell’era dei CSS? Per molti sembra qualcosa di sbagliato o inopportuno. Non usare le tabelle per il layout della pagina non significa escludere completamente il loro impiego. Le tabelle si possono usare anche in layout basati sui CSS, purchè siano usate correttamente!

Questo post è una traduzione autorizzata dall’autore, Roger Johansson, dell’articolo Bring on the tables, in cui si parla di come rendere l’utilizzo delle tabelle accessibile e corretto.
Secondo l’autore, bisogna evitare il più possibile l’utilizzo delle tabelle per elementi di layout, ma per mostrare dei dati tabulari, cioè informazioni presentate logicamente in righe e colonne, le tabelle sono lo strumento adatto!. Le tabelle mettono a disposizione molti più tag rispetto ai canonici td e tr, specialmente se si vuole rendere il contenuto più accesibile (e semantico!).

Ecco, per prima cosa, un po’ di informazioni generali.
L’affermazione "evitare l’uso di tabelle per il layout" si trova in "Introduzione alle tabelle" nelle specifiche HTML 4.01:

Tables should not be used purely as a means to layout document content as this may present problems when rendering to non-visual media. Additionally, when used with graphics, these tables may force users to scroll horizontally to view a table designed on a system with a larger display. To minimize these problems, authors should use style sheets to control layout rather than tables.

Direi che il testo è abbastanza chiaro, anche se la parola usata è "should", non "must": le specifiche tecniche consentono una certa flessibilità.

Ma questo articolo non vuole parlare della questione se utilizzare le tabelle per il layout o no. Tratta dell’utilizzo delle tabelle per la loro funzione originaria: la marcatura di dati tabulari.

Quando le tabelle vengono utilizzate per contrassegnare i dati effettivi, non sono solo una griglia per il layout. Le persone senza problemi di vista possono farsi un’idea del rapporto che c’è tra l’intestazione e le celle di dati osservando il layout e la presentazione visiva della tabella. Al contrario, le persone non vedenti o ipovedenti non possono farlo. Affinchè una tabella sia accessibile a persone che usano uno screen reader o qualche altro user-agent non-visivo, bisogna comunicare all’user-agent che rapporto hanno tra loro le informazioni che essa contiene.

Fortunatamente, l’HTML fornisce in abbondanza elementi e attributi per questo scopo. Meno fortunatamente, può essere piuttosto difficile capire come usare alcune di queste caratteristiche di accessibilità. In questo articolo cercherò di spiegare come utilizzarne la maggior parte.

Intestazione della tabella: il tag th

Cominciamo con una tabella molto semplice, che ha solo una singola riga di intestazioni, ognuna delle quali definisce i dati in una colonna.
Ecco come appare il codice contrassegnato alla vecchia maniera, con solo le righe e le celle:

<table>
  <tr>
    <td>Compagnia</td>
    <td>Dipendenti</td>
    <td>Fondazione</td>
  </tr>
  <tr>
    <td>ACME Inc</td>
    <td>1000</td>
    <td>1947</td>
  </tr>
  <tr>
    <td>XYZ Corp</td>
    <td>2000</td>
    <td>1973</td>
  </tr>
</table>

Senza bordi o stili, nella maggior parte dei browser la tabella sarà più o meno così:

Compagnia Dipendenti Fondazione
ACME Inc 1000 1947
XYZ Corp 2000 1973

Per una persona senza problemi di vista, è facile e veloce fare il collegamento tra le intestazioni e le celle di dati che esse descrivono. Chi utilizza uno screen reader, d’altra parte, avrebbe sentito qualcosa del tipo "Compagnia Dipendenti ACME Fondata Inc 1000 1947 2000 1073 XYZ Corp". Non è molto facile trovare un senso in queste parole!!!

Il primo – e più semplice – passo per rendere più accessibile questa tabella, è quello di marcare le intestazioni in modo corretto.
E’ molto facile: basta utilizzare l’elemento th (Table Header: Intestazione di Tabella) invece di td (Table Data: Dati tabellari) per le celle di intestazione:

<table>
  <tr>
    <th>Compagnia</th>
    <th>Dipendenti</th>
    <th>Fondazione</th>
  </tr>
  <tr>
    <td>ACME Inc</td>
    <td>1000</td>
    <td>1947</td>
  </tr>
  <tr>
    <td>XYZ Corp</td>
    <td>2000</td>
    <td>1973</td>
  </tr>
</table>
Compagnia Dipendenti Fondazione
ACME Inc 1000 1947
XYZ Corp 2000 1973

Per questa semplice tabella, aver inserito le intestazioni è sufficiente per permettere a uno screen reader di informare l’utente su quale cella di intestazione dei dati è legata a quale cella. Un lettore di schermo potrebbe dire "Azienda: ACME Inc. dipendenti: 1000. Fondazione: 1947", e così via per ogni riga. Molto meglio, no?

Inserire le didascalie: caption

L’elemento caption può essere utilizzato per fornire una breve descrizione della tabella, in modo molto simile a alla funzione che ha una didascalia per l’immagine.
Come impostazione predefinita, la maggior parte dei browser rendono il caption in posizione centrata sopra la tabella.
La proprietà CSS caption-side può essere utilizzata per modificarlo, se necessario. La maggior parte dei browser visualizzerà la didascalia al di sopra (top) o sotto (bottom) il contenuto della tabella, mentre alcuni accettano anche i valori sinistra o destra. Vi lascio sperimentare!

Quando viene utilizzato, il caption deve comparire per primo subito dopo l’apertura del tag table, per esempio, così:

<table>
  <caption>Tabella 1: dati della Compagnia</caption>
  <tr>
    <th>Compagnia</th>
    <th>Dipendenti</th>
    <th>Fondazione</th>
  </tr>
  <tr>
    <td>ACME Inc</td>
    <td>1000</td>
    <td>1947</td>
  </tr>
  <tr>
    <td>XYZ Corp</td>
    <td>2000</td>
    <td>1973</td>
  </tr>
</table>
Tabella 1: dati della Compagnia
Compagnia Dipendenti Fondazione
ACME Inc 1000 1947
XYZ Corp 2000 1973

È possibile utilizzare i CSS per lo stile della didascalia, se lo si desidera. Tuttavia, bisogna essere consapevoli del fatto che potrebbe essere necessario usare alcuni trick per mantenere la coerenza di layout su tutti i browser. Vi lascio esercitare anche su questo!

L’attributo summary: come spiegare il contenuto della tabella!

Una persona vedente può facilmente decidere se studiare una tabella in dettaglio. Un rapido sguardo ci dirà quanto è grande la tabella, e più o meno ciò che contiene.
Una persona che utilizza uno screen reader invece non può farlo, a meno che il webdesigner non aggiunga alla tabella un attributo che sintetizzi il contenuto. In questo modo è possibile fornire una descrizione più dettagliata della tabella rispetto a quanto faccia l’elemento caption.

Il contenuto dell’attributo summary non viene reso visibile dai browser visuali: in questo modo è possibile scrivere una descrizione abbastanza lunga, per fare in modo che chi ascolta capisca a pieno di cosa parla la tabella. Non bisogna però esagerare: utilizziamo l’attributo di sintesi solo quando è necessario, cioè per più tabelle complesse, per cui una sintesi sarà più facile, per chi utilizza un lettore di schermo, comprendere il contenuto della tabella. Ecco la tabella di prima con il tag summary:

<table summary="Il numero di dipendenti e l'anno di
 fondazione di alcune compagnie immaginarie.">
  <caption>Tabella 1: dati della Compagnia</caption>
  <tr>
    <th>Compagnia</th>
    <th>Dipendenti</th>
    <th>Fondazione</th>
  </tr>
  <tr>
    <td>ACME Inc</td>
    <td>1000</td>
    <td>1947</td>
  </tr>
  <tr>
    <td>XYZ Corp</td>
    <td>2000</td>
    <td>1973</td>
  </tr>
</table>

Come accorciare le intestazioni? Con l’attributo abbr!

Quando uno screen reader incontra una tabella, può annunciare l’intestazione associata (o header) prima di ogni cella di dati.
Se la tabella dispone di intestazioni lunghe però, sentendole ripetere più e più volte, può risultare noioso per chi ascolta. Utilizzando l’attributo abbr per fornire una versione abbreviata di eventuali intestazioni lunghe, potete dare agli screen reader qualcosa che possono utilizzare al posto del testo nella stessa intestazione. L’uso dell’attributo abbr è opzionale, e la maggior parte delle volte gli header saranno (o meglio, dovrebbero essere!) comunque piuttosto brevi.

La tabella di esempio è stata leggermente modificata, per rendere le intestazioni più lunghe. Vediamo come viene utilizzato l’attributo abbr:

<table summary="Il numero di dipendenti e l'anno di
 fondazione di alcune compagnie immaginarie.">
<caption>Table 1: Compagnia data</caption>
  <tr>
    <th abbr="Compagnia">Compagnia Name</th>
    <th abbr="Impiegati">Numero di Impiegati</th>
    <th abbr="Fondazione">Anno di fondazione</th>
  </tr>
  <tr>
    <td>ACME Inc</td>
    <td>1000</td>
    <td>1947</td>
  </tr>
  <tr>
    <td>XYZ Corp</td>
    <td>2000</td>
    <td>1973</td>
  </tr>
</table>

Uno screen reader leggerà quindi leggere le intestazioni (con la descrizione lunga) per la prima riga dei dati, per poi utilizzare l’abbreviazione per le righe rimanenti.

Considerando quanto difficile possa essere l’inserimento di tabelle nei layout web, direi che è più comune l’esigenza contraria: per rendere le intestazioni più brevi possibile, o anche abbreviate, è meglio utilizzare l’attributo title o l’elemento abbr per fornire una spiegazione più lunga.

Come collegare le intestazioni con i dati?
Con gli attributi scope, ID e headers

Molte tabelle sono più complesse rispetto alla tabella di esempio che abbiamo visto finora. La renderemo un po’ più complessa, eliminando l’intestazione "Compagnia" e modificando le celle di dati nella prima colonna, e nelle celle dell’intestazione

<table summary="Il numero di dipendenti e l'anno di
 fondazione di alcune compagnie immaginarie.">
  <caption>
  Table 1: Compagnia data
  </caption>
  <tr>
    <td></td>
    <th>Dipendenti</th>
    <th>Fondazione</th>
  </tr>
  <tr>
    <th>ACME Inc</th>
    <td>1000</td>
    <td>1947</td>
  </tr>
  <tr>
    <th>XYZ Corp</th>
    <td>2000</td>
    <td>1973</td>
  </tr>
</table>
tabella 1: dati della Compagnia
Dipendenti Fondazione
ACME Inc 1000 1947
XYZ Corp 2000 1973

In questa tabella, ogni cella di dati ha due intestazioni. Il metodo più semplice, cioè per un markup più "saggio", per fare in modo che un browser non-visivo possa dare un senso a questa tabella, è quello di aggiungere un attributo scope a tutte le celle di intestazione:

<table summary="Il numero di dipendenti e l'anno di 
fondazione di alcune compagnie immaginarie.">
  <caption>
  Table 1: Compagnia data
  </caption>
  <tr>
    <td></td>
    <th scope="col">Dipendenti</th>
    <th scope="col">Fondazione</th>
  </tr>
  <tr>
    <th scope="row">ACME Inc</th>
    <td>1000</td>
    <td>1947</td>
  </tr>
  <tr>
    <th scope="row">XYZ Corp</th>
    <td>2000</td>
    <td>1973</td>
  </tr>
</table>

L’attributo scope definisce se una cella di intestazione fornisce informazioni di intestazione per una colonna o una riga:

  • col: informazioni di intestazione per la colonna in cui si trova
  • row: informazioni di intestazione per la riga in cui si trova

L’aggiunta di un attributo scope con il valore col alle intestazioni nella prima riga dichiara che sono le intestazioni per le celle di dati sotto di esse.
Allo stessa maniera, dando uno scope con valore row alle intestazioni che iniziano ogni riga, le rendiamo intestazioni per tutte le celle di dati che si trovano alla loro destra.

L’attributo scope può assumere altri due valori:

  • colgroup: informazioni di intestazione per il resto del gruppo di colonne che la contiene
  • rowgroup: informazioni di intestazione per il resto del gruppo di righe che la contiene

Un gruppo di colonne è definito dall’ elemento colgroup. Ragruppamenti di rige sono definiti dagli elementi thead , tfoot e tbody.
Ma di quest’ultimi torneremo a parlare tra poco.

Che cosa succede se si desidera: sia mantenere l’intestazione "Compagnia", e che i nomi delle società siano le intestazioni di riga?
Ciò farebbe sì che le celle che contengono i nomi delle società debbano fornire sia le informazioni di intestazione, sia i dati. In questo caso, td dovrebbe essere usato assieme all’attributo scope:

<table summary="Il numero di dipendenti e l'anno di 
fondazione di alcune compagnie immaginarie.">
  <caption>
  Table 1: Compagnia data
  </caption>
  <tr>
    <th scope="col">Compagnia</th>
    <th scope="col">Dipendenti</th>
    <th scope="col">Fondazione</th>
  </tr>
  <tr>
    <td scope="row">ACME Inc</td>
    <td>1000</td>
    <td>1947</td>
  </tr>
  <tr>
    <td scope="row">XYZ Corp</td>
    <td>2000</td>
    <td>1973</td>
  </tr>
</table>

In questo modo, nei browser visuali non verranno visualizzati i nomi delle società come intestazioni di default, quindi è necessario un po’ di CSS per rimediare.
Per questo esempio, ho usato il seguente codice CSS:

td[scope] {
	font-weight:bold;
}

Si noti che questa regola utilizza un selettore di attributo, che Internet Explorer non supporta. Una soluzione sarebbe aggiungere una classe a qualsiasi celle di dati da stilare come intestazione.

Un’altra tecnica per collegare le celle di una tabella con l’intestazione cui fanno riferimento comporta il dover dare a ogni intestazione un ID univoco.
Va poi aggiunto ad ogni cella un attributo headers. Questo attributo contiene un elenco, separato da spazi, degli id di ogni cella di intestazione che si applica a quella cella di dati. Questa tecnica è più complicata, e deve essere utilizzata solo quando le celle di dati che devono essere collegate a più di due celle di intestazione e l’attributo scope è insufficiente, come può accadere in una tabella molto complessa o irregolare.

Per illustrare questo caso, ho cambiato la tabella in modo tale da mostrare, per ogni socità, il numero di dipendenti di ciascun sesso:

<table class="extbl" summary="Il numero di dipendenti e l'anno di 
fondazione di alcune compagnie immaginarie.">
  <caption>
  Tabella 1: dati della Compagnia
  </caption>
  <tr>
    <td rowspan="2"></td>
    <th id="employees" colspan="2">Dipendenti</th>
    <th id="founded" rowspan="2">Fondazione</th>
  </tr>
  <tr>
    <th id="men">Uomini</th>
    <th id="women">Donne</th>
  </tr>
  <tr>
    <th id="acme">ACME Inc</th>
    <td headers="acme employees men">700</td>
    <td headers="acme employees women">300</td>
    <td headers="acme founded">1947</td>
  </tr>
  <tr>
    <th id="xyz">XYZ Corp</th>
    <td headers="xyz employees men">1200</td>
    <td headers="xyz employees women">800</td>
    <td headers="xyz founded">1973</td>
  </tr>
</table>
Tabella 1: dati della Compagnia
Dipendenti Fondazione
Uomini Donne
ACME Inc 700 300 1947
XYZ Corp 1200 800 1973

Come si può ben vedere, questo metodo diventa rapidamente molto complicato, quindi se è possibile, è meglio utilizzare l’attributo scope.

Raggruppare celle con rowspan e colspan

Nel vecchio approccio web (layout con tabelle), gli attributi rowspan e colspan erano spesso usati per unire le celle delle tabelle in diverse righe o colonne, al fine di metterci dentro, in fila una dopo l’altra, tutte le immagini ritagliate. Tali attributi sopravvivono ancora, dato che non vi è alcun modo per usare i CSS per specificare lo "spanning".
Se ci pensate bene, è abbastanza logico: le fusioni di righe e di colonne sono parte della struttura di una tabella, non sono la sua presentazione!

Colonne e gruppi di colonne: col e colgroup

L’HTML mette a disposizione gli elementi colgroup e col per raggruppare colonne correlate . Questo permette (in alcuni browser) l’uso di CSS per dare alle colonne dello stile in modo indipendente. Per fare gruppi di colonne, può essere utilizzato anche l’attributo scope, per specificare che una cella contiene informazioni di intestazione per il resto del gruppo di colonne che la contiene.

Questo è tutto ciò che diremo in questo articolo su colonne e gruppi di colonne.
Per informazioni più dettagliate, date un’occhiata ai collegamenti nella sezione "What I didn’t tell you" dell’articolo originale, oppure in fondo a questa pagina.

Creare gruppi di righe: <caption>, <thead>, <tfoot>, <tbody>

Le righe della tabella possono essere raggruppate in una sezione intestazione di tabella racchiusa nei tag thead, in una sezione piè pagina nel tag tfoot, e in uno o più sezioni corpo della tabella mediante il tag tbody. Ogni gruppo di righe deve contenere una o più righe di tabella.

Se una tabella contiene una sezione di testa thead, quest’ultima deve comparire prima del piè pagina e delle sezioni del corpo tabellare.
La sezione tfoot deve sempre comparire prima del (o dei) tag tbody.
Se nella tabella non è usata nè theadtfoot, l’elemento tbody non è richiesto (ma non vietato: potete aggiungerlo, se volete).

La struttura di una tabella che contiene i gruppi di righe appare così:

<table>
  <thead>
    <tr></tr>
  … altre righe per la table-head
  </thead>
  <tfoot>
    <tr></tr>
  … altre righe per la table-foot
  </tfoot>
  <tbody>
    <tr></tr>
  … altre righe per la table-body
  </tbody>
  <tbody>
    <tr></tr>
  … altre righe per la seconda table-body
  </tbody>
  … altre righe per le altre eventuali table-body
</table>

Raggruppare le righe è essere utile per diversi motivi:

  1. Si può facilmente applicare lo stile al tfoot, thead, e alle sezioni tbody di una tabella in modo indipendente l’uno dall’altro, senza dover aggiungere classi.
  2. Quando si stampano tabelle molto lunghe, alcuni browser (come quelli basati su Mozilla) ripeteranno le informazioni dell’intestazione e del piè pagina in ogni pagina stampata, rendendo più facile leggere la tabella stampata.
  3. Separare l’intestazione e il piè pagina dal corpo vero e proprio permette anche ad alcuni browser di scorrere solo il corpo della tabella.

Solo per le tabelle di dati

Tutto quanto fin qui descritto riguarda l’uso di tabelle HTML per strutturare e presentare i dati. Se si utilizzano tabelle per il layout, nessuna delle tecniche qui descritte qui dovrebbero essere usate. Nessun attributo summary, nessuna intestazione, né caption, niente. Solo una semplice, tabella con layout vecchio stile, composta nientaltro che dai tag table, tr e td.
Altrimenti si rischia di confondere ancora di più gli utenti che utilizzano screen-reader.

I vantaggi

Può sembrare un sacco di lavoro per creare tabelle di dati accessibili in HTML. Per le tabelle complesse, lo è. A volte al punto in cui diventa quasi impossibile scriverle manualmente.
Per le tabelle semplici, però, utilizzare celle di intestazione con un attributo scope è veloce e facile.

E ‘ovvio che le persone che usano screen-reader o altra tecnologia assistiva, trovano beneficio dalle tabelle che utilizzano le funzioni di accessibilità disponibili. Cercare di dare un senso a un grande e complessa tabella per chi ascolta può ancora essere molto difficile, quindi, se possibile, cerchiamo di semplificare la tabella.

Meno è ovvio che traggono beneficio anche i progettisti e gli utilizzatori di browser grafici: una tabella accessibile è ricca di ganci strutturali da applicare ai CSS, e uno stile scritto bene può rendere la tavola più fruibile per tutti.

Quello che non vi ho detto

Ma ci sarebbe molto altro da dire sulle tabelle di dati che qui non ho citato. Per esempio, non ho menzionato per niente (fino ad ora) l’attributo axis, e non ho descriitto nel dettaglio gli elementi colgroup e col. Non ho nemmeno accennato alla formattazione e allo stile, o ai tipi di bordo. Inoltre, manca un esempio di una tabella estremamente complessa.

Per quelli di voi che cercano informazioni più dettagliate, ecco dei link ad alcuni ulteriori approfondimenti:

Articolo originale

Bring on the tables di Roger Johansson