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
) :
Après (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;}
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 largeurswidth
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 queword-wrap
ouhyphens
. - 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 :
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
:
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 :
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 (valeurseparate
) ou fusionnées (valeurcollapse
). border-spacing
- La propriété CSS
border-spacing
, reconnue à partir d’Internet Explorer 8 (et équivalente à l’attribut HTMLcellspacing
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 valeurcollapse
. empty-cells
- La propriété
empty-cells
(à ne pas confondre avec le sélecteur CSS3:empty
) gère l’affichage des cellules vides : la valeurhide
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.
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) etbottom
, mais aussileft
etright
, même si ces dernières ne sont actuellement comprises que par Firefox.
div {
display: table;
border-collapse: separate;
}
div {
display: table;
border-collapse: separate;
border-spacing: 20px 5px;
}
div {
display: table;
empty-cells: hide;
}
div {
display: table;
caption-side: bottom;
}
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 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.
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;
}
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éclarationcaption-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 :
Affublé d'un caption-side: bottom
, l'élément table-caption
se place en dessous :
Des éléments table-header-group
et table-footer-group
s'affichent ainsi :
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 untable-row
, alors un objet anonymetable-row
est créé autour des frèrestable-cell
- Si le parent d’éléments frères
table-row
(ou équivalent) n’est pas untable
, alors un objet anonymetable
est créé autour des frèrestable-row
ou équivalents
Illustration : un élément table-cel
l construit autour de lui un objet table-row
et table
, ses frères blocks ne sont pas concernés :
Un objet table-row
+ table
se construit autour de 2 frères table-cell
:
Même schéma pour trois frères puis quatre frères (illustrations ci-dessous) :
Construction anonyme ascendante :
-
Un parent
table
(ou équivalent) construit un objet anonymetable-row
autour de l'ensemble de ses enfants directs qui ne sont pastable-row
ou équivalents -
Un parent
table-row
(ou équivalent) construit un objet anonymetable-cell
autour de l'ensemble de ses enfants directs qui ne sont pastable-cell
Illustration : un parent table
construit autour des 4 blocks un objet table-cell
+ table-row
:
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) :
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 :
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
, nimargin
,position, width, min/max-width, min/max-height, vertical-align, border
etoverflow
.- Plus globalement, il n’est pas possible d’appliquer la propriété
padding
aux élément endisplay table-row-group, table-header-group, table-footer-group, table-row, table-column-group
ettable-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.
L'élément «choucroute» est positionné en position: absolute
à right: 0
et top: 0
. Logiquement, il se positionne hors du tableau :
Lorsque le conteneur global (display: table
) est en position: relative
, il devient un référent pour «choucroute» :
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.
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
:
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
:
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
:
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
- Spécifications officielles CSS2.1
- Alternative à table-cell pour IE6 et IE7
- Conférence à Paris-Web 2011 sur les tableaux de mise en page
- Livre de Sitepoint «Everything You Know About CSS Is Wrong!» (Rachel Andrew & Kevin Yanks, SitePoint, 2008)
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 !
# Long Lazuli Le 10 mars 2015 à 14:08
Chouette article !
Nous avons appris à séparer la sémantique de la présentation,
mais il y a des cas ou la présentation que nous cherchons est proche d’un tableau !
Je vous propose un exemple d’utilisation :
http://codepen.io/long-lazuli/pen/auFyz
: : Afficher des lignes autour de titres sans ajouter des éléments ni modifier la sémantique.
Vos commentaires
Suivre les commentaires : |