Le modèle tabulaire en CSS

Openweb.eu.org > Articles  > Le modèle tabulaire en CSS

Abstract

Le modèle de construction tabulaire (HTML et CSS) compte sans aucun doute parmi les plus fascinants qui soient, après avoir été d’abord adulé puis banni vigoureusement par les intégrateurs de tous poils.

En 1998, les spécifications CSS2 étendent les historiques valeurs de la propriété display et proposent depuis belle lurette des possibilités de rendus et positionnements « tabulaires » sans pour autant interférer dans la sémantique et le bon usage des éléments HTML.

Passons immédiatement à table et découvrons en détail le monde mal connu du Modèle Tabulaire en CSS

Article

Note : Cet article est publié conjointement sur OpenWeb.eu.org et sur Alsacreations.com. En outre, certaines parties de cet article sont extraites du livre «CSS avancées, vers HTML5 et CSS3», avec l’aimable autorisation de l’auteur :)

Compatibilité

Commençons par une excellente nouvelle : le modèle de rendu tabulaire en CSS est finalisé depuis suffisamment longtemps pour être compatible avec tous les navigateurs actuels et leurs générations précédentes.

Il faut remonter aux antiques versions d’Internet Explorer 7 et précédentes pour trouver des navigateurs qui ne supportent pas ce schéma de positionnement.

Display

Depuis les spécifications CSS2, pas moins de 10 valeurs dédiées aux rendus tabulaires ont été ajoutées à la propriété display.

Vous connaissiez sans nul doute déjà les valeurs block, inline, none et inline-block, en voici de nouvelles dans notre arsenal à présent bien complet :

table
Spécifie un comportement de table de type bloc pour un élément. C’est le rendu par défaut des éléments <table>
inline-table
Spécifie un comportement de table de type en-ligne pour un élément.
table-row
Spécifie que l'élément s’affiche comme une rangée de cellules. C’est le rendu par défaut des éléments <tr>
table-row-group
Spécifie qu'un élément regroupe une ou plusieurs rangées. C’est le rendu par défaut des éléments <tbody>
table-header-group
S’affiche comme table-row-group, mais ce groupe de rangées est toujours affiché avant toutes les autres rangées et groupes de rangées. C’est le rendu par défaut des éléments <thead>
table-footer-group
S’affiche comme table-row-group, mais ce groupe de rangées est toujours affiché après toutes les autres rangées et groupes de rangées. C’est le rendu par défaut des éléments <tfoot>
table-column
Spécifie qu'un élément représente une colonne de cellules. C’est le rendu par défaut des éléments <col>
table-column-group
Spécifie qu'un élément regroupe une ou plusieurs colonnes. C’est le rendu par défaut des éléments <colgroup>
table-cell
Spécifie qu'un élément doit s’afficher tel une cellule de table. C’est le rendu par défaut des éléments <th> et <td>
table-caption
Spécifie le rendu d’une légende d'une table. C’est le rendu par défaut des éléments <caption>

En action !

Passons immédiatement à la pratique, et procédons à quelques tests de routine.

Prenons au hasard deux éléments «block» classiques, disons deux <div>, et modifions simplement la valeur de leur propriété intrinsèque display (block) par la valeur «table-cell» :

div {display: table-cell;}

Avant (block) :

display block par défaut

Après (table-cell) :

display table-cell

Les deux éléments s’affichent à présent l’un à côté de l’autre tels des éléments inline. Mais contrairement à un rendu inline, ces éléments table-cell peuvent être dimensionnés :

div {display: table-cell;}
div:first-child {width: 150px;}

display table-cell dimensionné

Nous n’avons pas modifié la sémantique des éléments HTML, ce sont toujours des <div>, nous n’avons fait que modifier leur rendu par défaut via CSS, comme nous l’aurions fait en passant de display: block à display: inline par exemple.

Algorithme de calcul des tableaux

Contrairement à la croyance commune, ce n'est pas un mais un double algorithme qui définit le rendu des structures tabulaires :

  • Le modèle automatique (table-layout: auto) appliqué par défaut confère le pouvoir aux contenus des cellules. Ces derniers déterminent la largeur des colonnes : plus le contenu est dense, plus la colonne est large. Les largeurs width renseignées pour la table ou ses cellules ne seront qu’indicatives.
  • Le modèle fixe (table-layout: fixed) se base sur la largeur réelle du tableau et de ses colonnes, et ne dépend pas du contenu.

Définir l’algorithme d’affichage en mode fixe a plusieurs avantages :

  • Dès qu’un élément de colonne a une valeur width, alors cette valeur est retenue pour la largeur de toute la colonne. Les éventuelles colonnes restantes se partagent équitablement l’espace horizontal restant de la table.
  • Le navigateur peut commencer à afficher la table dès la réception de la première rangée. De manière générale, cet algorithme accélère les traitements et l’affichage par le navigateur (pas de reflow inutile).
  • Les contenus n’affectent pas les largeurs des colonnes. Toute cellule s’appuie sur la propriété overflow pour déterminer le rognage, ou non, du contenu qui déborderait, ou encore des propriétés de césure telles que word-wrap ou hyphens.
  • Dans le cas de tableaux de données complexes, mon expérience m’a appris à apprivoiser ce mode de rendu plus strict et robuste que le rendu automatique… et qui est reconnu dès Internet Explorer 5 (et les autres navigateurs également, bien entendu).

Prenons pour exemple : un parent tabulaire contenant trois enfants en table-cell. Les dimensions (width) de chaque élément sont spécifiés en CSS : 600px pour le parent et 200px pour chacun des trois enfants :

table-layout auto

En ajoutant une image de plus de 200 pixels de large au sein de la première cellule, celle-ci s'étire ainsi que tout l'ensemble du tableau. Le contenu est roi, c'est le comportement par défaut de table-layout: auto :

table-layout auto avec un élément plus large

En faisant bénéficier le parent d'un table-layout: fixed, chaque élément conserve ses largeurs définies, et le contenu déborde comme dans n'importe quel autre élément HTML :

table-layout fixed avec un élément plus large

Autres propriétés spécifiques aux tableaux

À l’instar de table-layout, certaines autres propriétés CSS sont spécifiques aux éléments de tableaux. Il s’agit de border-collapse, border-spacing, empty-cells et caption-side.

border-collapse
Comme c’est le cas pour les tableaux HTML, la propriété border-collapse détermine si les bordures de la table et entre les cellules doivent être séparées (valeur separate) ou fusionnées (valeur collapse).
div {
	display: table;
	border-collapse: separate;
}
border-spacing
La propriété CSS border-spacing, reconnue à partir d’Internet Explorer 8 (et équivalente à l’attribut HTML cellspacing devenu obsolète), spécifie la distance qui sépare les bordures de cellules adjacentes.
Lorsque deux valeurs sont renseignées, la première désigne l’espacement horizontal et la deuxième le vertical. La valeur de cette propriété devient automatiquement nulle lorsque border-collapse a pour valeur collapse.
div {
	display: table;
	border-collapse: separate;
	border-spacing: 20px 5px;
}
empty-cells
La propriété empty-cells (à ne pas confondre avec le sélecteur CSS3 :empty) gère l’affichage des cellules vides : la valeur hide masque les bordures de la cellule.
Lorsque cette propriété est appliquée au sein d’une table ne comportant qu’une seule rangée, les cellules vides disparaissent complètement et les autres cellules se réorganisent sans elles. Cette propriété est prise en compte à partir d’Internet Explorer 8 et chez les autres navigateurs, bien entendu.
div {
	display: table;
	empty-cells: hide;
}
caption-side
Cette propriété, reconnue elle aussi depuis IE8, indique la position de la boîte de la légende en fonction de celle de la table. Les valeurs acceptées sont top (par défaut) et bottom, mais aussi left et right, même si ces dernières ne sont actuellement comprises que par Firefox.
div {
	display: table;
	caption-side: bottom;
}

border-spacing: 20px

border-spacing: 20px 0

Distribution par défaut

La disposition des éléments d’un tableau se fait sous la forme d’un quadrillage composé de rangées et de colonnes.

Les éléments internes d’une table n’ont pas de marges externes (margin), et les éléments de rangées ne disposent pas non plus de marges internes (padding).

Distribution verticale

Les rangées remplissent l’ensemble de la table, du haut vers le bas dans l’ordre de leur apparition dans le code source.

Chaque cellule d’une même rangée s’adapte automatiquement à la hauteur de la cellule la plus haute, pour occuper toute la hauteur de la rangée.

distribution verticale

Distribution horizontale

Les cellules d’une rangée remplissent l’ensemble de l’espace de leur rangée, de la gauche vers la droite dans l’ordre d’apparition dans le code HTML (sauf si la propriété direction est appliquée sur le tableau).

Une cellule ne peut pas recouvrir une autre cellule et ne peut pas s’étendre au-delà de sa rangée.

distribution horizontale 1

distribution horizontale 2

distribution horizontale 3

Alignement vertical

Les cellules de tableau HTML (<td>) sont réputées pour bénéficier d’un avantage tenant du Saint Graal du concepteur web : pouvoir appliquer la propriété vertical-align et en tirer toutes les vertus en termes d’alignement vertical des contenus.

Qu’à cela ne tienne ! vertical-align est parfaitement adéquate pour le modèle tabulaire CSS et peut être affectée à un élément en display: table-cell pour en aligner le contenu.

Un vieux rêve d’intégrateur web se réalise sous nos yeux : celui de pouvoir intuitivement centrer verticalement n’importe quel élément en HTML.

div {
	display: table-cell;
	vertical-align: middle;
}

alignement vertical

Les valeurs acceptées pour l’alignement vertical sont :

baseline
La ligne de base de la cellule se place à la même hauteur que celle de la première rangée dans laquelle celle-ci s'étend.
top
Le haut de la boîte de la cellule s'aligne sur le haut de la première rangée dans laquelle celle-ci s'étend.
bottom
Le bas de la boîte de la cellule s'aligne sur celui de la dernière rangée dans laquelle celle-ci s'étend.
middle
Le milieu de la cellule s'aligne sur celui des rangées dans lesquelles celle-ci s'étend.
sub, super, text-top, text-bottom
Ces valeurs ne s'appliquent pas aux cellules ; pour ces valeurs, la cellule s'aligne sur la ligne de base (baseline) à la place.

Plus d’informations : Comment aligner verticalement une image et une ligne de texte

Flux et ordre d'affichage

Certaines valeurs spécifiques du modèle d’affichage sous forme de tableau modifient l’ordre d’affichage des éléments empilés verticalement :

  • Un élément déclaré en table-header-group se placera avant ses frères.
  • Un élément en table-footer-group se positionnera à la suite de ses frères.
  • Un élément en table-caption apparaîtra par défaut tout en haut de la pile, mais peut être déplacé tout en bas s’il est accompagné d’une déclaration caption-side: bottom.

Illustration : un élément en table-caption s'affiche avant son frère, la boîte anonyme table construite autour des 3 blocks :

table-caption

Affublé d'un caption-side: bottom, l'élément table-caption se place en dessous :

table-caption et caption-side

Des éléments table-header-group et table-footer-group s'affichent ainsi :

table-header-group et table-footer-group

Les éléments anonymes

Chaque élément de rendu tableau en CSS crée automatiquement des objets de table anonymes autour de lui-même, c’est-à-dire que les éléments de structure manquants sont créés par le navigateur.

Ce concept est assez complexe à appréhender et pourtant essentiel pour bien comprendre les subtilités des positionnements tabulaires.

Construction anonyme descendante :

  • Si le parent d’éléments frères table-cell n’est pas un table-row, alors un objet anonyme table-row est créé autour des frères table-cell
  • Si le parent d’éléments frères table-row (ou équivalent) n’est pas un table, alors un objet anonyme table est créé autour des frères table-row ou équivalents

Illustration : un élément table-cell construit autour de lui un objet table-row et table, ses frères blocks ne sont pas concernés :

construction anonyme 1

Un objet table-row + table se construit autour de 2 frères table-cell :

construction anonyme 2

Même schéma pour trois frères puis quatre frères (illustrations ci-dessous) :

construction anonyme 3

construction anonyme 4

Construction anonyme ascendante :

  • Un parent table (ou équivalent) construit un objet anonyme table-row autour de l'ensemble de ses enfants directs qui ne sont pas table-row ou équivalents
  • Un parent table-row (ou équivalent) construit un objet anonyme table-cell autour de l'ensemble de ses enfants directs qui ne sont pas table-cell

Illustration : un parent table construit autour des 4 blocks un objet table-cell + table-row :

construction anonyme 5

Un des éléments est un table-cell ; autour des 3 blocks frères se construit un table-cell également, puis un table-row se contruit autour des 2 table-cell (celle de gauche et celle - anonyme - de droite) :

construction anonyme 6

Dans les illustrations suivantes, un élément table-cell anonyme est construit autour des éléments frères blocks, puis un table-row anonyme se construit autour des cellules réelles ou imaginaires :

construction anonyme 7

construction anonyme 8

construction anonyme 9

Spécificités des éléments tabulaires


Propriétés incompatibles

Les valeurs de rangées (display: table-row et équivalents) présentent des particularités liées à leur rendu :

  • table-row ne comprend pas la propriété padding, ni margin, position, width, min/max-width, min/max-height, vertical-align, border et overflow.
  • Plus globalement, il n’est pas possible d’appliquer la propriété padding aux élément en display table-row-group, table-header-group, table-footer-group, table-row, table-column-group et table-column.
  • Enfin, il ne peut y avoir qu’un seul table-caption au sein d’un tableau.

Positionnement

Selon les spécifications, positionner ou rendre flottant une cellule de tableau *peut* modifier son comportement, ne plus être considérée comme une cellule et affecter l’alignement et les dimensions de la table.

En pratique, il n’est pas possible de faire bénéficier les éléments tabulaires de la position relative sur certains navigateurs (Firefox par exemple), ce qui peut être gênant puisqu’ils ne peuvent ainsi pas servir de référents pour des contenus positionnés en absolu.

positionnement 1

L'élément «choucroute» est positionné en position: absolute à right: 0 et top: 0. Logiquement, il se positionne hors du tableau :

positionnement 2

Lorsque le conteneur global (display: table) est en position: relative, il devient un référent pour «choucroute» :

positionnement 3

Par contre, lorsque l'un des élements en display: table-cell est en position: relative, il ne devient pas un référent pour «choucroute» sur certains navigateurs.

positionnement 4

Génération de contenu

Il est possible de générer du contenu en CSS sur des éléments tabulaires à l’aide des pseudo-éléments CSS3 ::before et ::after (ou leurs versions CSS2 :before et :after).

Particularité pittoresque : les éléments générés sont susceptibles de participer eux aussi au grand jeu de la construction d’objets anonymes (voir précédemment), et se voir inclus dans des éléments table-cell ou table-row.

Illustration : des éléments générés devant et derrière le contenu de l'enfant table-cell :

génération de contenu 1

div {display: table}
div:before {
	content: "div:before";
	display: table-cell;
	width: 33%;
	padding: 3px;
	background: orange;
}
div:after {
	content: "div:after";
	display: table-cell;
	width: 33%;
	padding: 3px;
	background: #39f; 
}

Si les éléments sont générés directement sur le parent en display: table, alors un objet anonyme table-cell se crée autour de chacun des éléments individuels générés en :before et :after :

génération de contenu 2

Si les éléments générés sont eux-mêmes en display: table-cell, ils participent avec leur troisième frère table-cell à la construction d'un conteneur anonyme commun table-row :

génération de contenu 3

Considérations d'accessibilité

Quelques tests de lecteurs d’écran ont révélé que certains outils d’accessibilité traitent les éléments en display: table (et variantes) comme de vrais éléments tablulaires HTML, même s’il s’agit de div ou de paragraphes.

Cela peut créer des gênes, voire des soucis d’accessibilité, lorsque ces éléments sont porteurs de sens tels que des éléments de navigation par exemple ou des niveaux de titres.

Aux dernières nouvelles, la grande majorité (Window-eye, Jaws, Voice-over et NVDA sur IE) se comportent correctement. Le problème (s'il s'agit vraiment d'un problème) n'apparaît que sur certains dispositifs tels que NVDA sur Firefox et ORCA.

Plus d’information : Using display:table has semantic effects in some screen readers

Conclusion

Avec ses avantages et inconvénients, le modèle tabulaire en CSS répond à des besoins de constructions que l'on peut rencontrer quotidiennement dans notre métier d'intégrateur.

Trop souvent laissé pour compte en raison du lourd passé de ses cousins (les <table> HTML), le positionnement CSS via display mérite aujourd'hui toute notre attention, depuis la mort programmée des anciennes versions d'Internet Explorer, même si tous nos maux ne seront pas résolus en un coup de… pinceau !

En tout cas, j'espère que vous ne pourrez plus dire désormais que «je n'y comprends rien aux tableaux CSS»…

Ressources

Démonstrations en ligne

À propos de cet article

  • Openweb.eu.org
  • Profil : Expert, Gourou
  • Technologie : CSS
  • Thème : Mise en page
  • Auteur :
  • Publié le :
  • Mise à jour : 30 décembre 2012
  • 1 commentaire

Vos commentaires

  • Nico Le 11 septembre 2013 à 16:28

    Merci pour cet article vraiment très instructif et très détaillé. C’est exactement ce qu’il me fallait pour enfin me mettre à jour avec les display.
    Adieu les float !

Répondre à cet article

Qui êtes-vous ?

Pour afficher votre trombine avec votre message, enregistrez-la d’abord sur gravatar.com (gratuit et indolore) et n’oubliez pas d’indiquer votre adresse e-mail ici.

Ajoutez votre commentaire ici
  • Ce formulaire accepte les raccourcis SPIP [->url] {{gras}} {italique} <quote> <code> et le code HTML <q> <del> <ins>. Pour créer des paragraphes, laissez simplement des lignes vides.

Suivre les commentaires : RSS 2.0 | Atom