display: inline-block et les espaces indésirables

Astucecss

Publié par le , mis à jour le (253581 lectures)

inline-block espace whitespace cadratin

La valeur inline-block de la propriété display est à la mode, même si elle demeure encore trop peu connue et mal utilisée. Elle offre de multiples avantages dont le principal est de pouvoir disposer des éléments les uns à côté des autres, tout en étant dimensionnés et sans les retirer du flux.

L'un de ses inconvénients majeurs est l'apparition d'un espace indésirable et incompressible de prime abord entre les blocs. S'il n'est pas gênant, tant-mieux; sinon, de multiples techniques plus biscornues les unes que les autres existent.

Le cas «white-space»

Comme tous les éléments de type inline ou de contenu textuel, les objets munis de la déclaration display: inline-block respectent la règle des caractères blancs (whitespace), c’est-à-dire que n’importe quel caractère HTML espaçant deux éléments va immanquablement apparaître entre leurs deux boîtes. Chaque caractère «espace», «tabulation», «nouvelle ligne», «retour chariot» ou «nouvelle page» peut tenir lieu de white-space et séparer les blocs visuellement.

whitespace

L’espace créé par le white-space est d'environ 4 pixels, mais ne vous y méprenez pas : cela varie selon les navigateurs et la taille de police.

Dans la grande majorité des cas, cette situation n’est guère gênante. Cependant, elle peut le devenir si votre présentation doit être calée au pixel près, par exemple pour un parent de 600 pixels de large devant contenir trois paragraphes de 200 pixels chacun :

#main {
   width: 600px
}
#main p {
   display: inline-block;
   width: 200px;
}

whitespace2

Démonstration

De nombreux concepteurs se sont penchés sur cette problématique, certains tentent de jouer avec les marges négatives, d’autres sur l’espace entre lettres (letter-spacing). Mais à l’heure actuelle, aucune solution ultime n’a été publiée.

La solution sûre : modification du code HTML

La première solution et la plus logique consiste à supprimer les white-space physiquement et directement au sein du code HTML. Cela peut se faire en «collant» les éléments les uns aux autres :

<div>
<p>Lorem Elsass ipsum</p><p>réchime amet sed bissame so libero knackes choucroute</p><p>Hopla kuglopf flammekueche</p>
</div>

Variante : commentaires HTML

On peut encore transformer les white-spaces en commentaires HTML, qui ne seront pas interprétés par les navigateurs mais préserveront visuellement les retours à la ligne, ce qui facilitera la lecture du code par un intégrateur :

</p><!--
--><p>

Ce qui donne au final :

<div>
<p>Lorem Elsass ipsum</p><!-- @whitespace
--><p>réchime amet sed bissame so libero knackes choucroute…</p><!-- @whitespace
--><p>Hopla kuglopf flammekueche</p>
</div>

Cette alternative, bien que parfois un peu «inesthétique» et nécessitant d'avoir accès aux fichiers HTML, se révèle sans aucun doute la plus robuste et la plus pérenne actuellement.

Démonstration

Les "bidouilles" CSS

Via CSS, il existe également certaines techniques permettant de contourner cet espacement non souhaité.

Aucune des méthodes décrites ci-dessous ne sera parfaite, je préfère vous l'annoncer sans détour, mais elles ont un avantage indéniable également : celui de corriger le problème sans avoir à toucher à la structure HTML, et en employant la technologie adaptée, à savoir CSS.

  1. Bidouille 1 : white-space: nowrap et overflow: hidden

  2. Bidouille 2 : letter-spacing négatif de 0.31em (méthode recommandée par Yahoo!)

  3. Bidouille 3 : margin-right négatif de -0.25em

La technique de font-size : 0 sur le parent

Une solution parmi les plus robustes consiste à appliquer une taille de police nulle au parent, puis à redéfinir la taille de police au sein de chacun des blocs.

L’unité appropriée sera forcément non relative (pixels par exemple) puisqu’une unité fluide telle que em, exprimée par rapport à l’unité parente, sera inopérante puisque égale à zéro :

Pour pallier le problème d'unité relatif, il est possible d'opter pour une unité introduite en CSS3 : rem (pour «root em»).

Une taille de police en rem n'est pas relative à la taille de police de son parent, mais à celle de la racine du document (donc l'élément <html> en langage HTML). Bien que reconnue par une majorité des navigateurs récents (depuis IE9), il lui est aisé de proposer une alternative en pixels pour les anciens navigateurs de cette manière :

#main {
   width: 800px;
   font-size: 0;
}
#main p {
   display: inline-block;
   width: 200px;
   font-size: 20px; font-size: 2rem;
}

L'intérêt principal est que la taille de police des éléments demeure fluide tel que le serait l'unité em.

Démonstration

La technique du framework Pure.io

PureCSS.io est un framework CSS mis à jour par Yahoo! et dont les grilles de mise en forme reposent sur le positionnement inline-block

Afin de contourner l'ensemble des problèmes dûs au whitespace, PureCSS a révélé sa technique au sein d'un billet de blog. Il s'agit d'une combinaison de plusieurs stratagèmes :

Sur le parent :

  • un letter-spacing négatif de -0.31em
  • une suppression de toutes ligatures et fioritures typographiques via text-rendering: optimizespeed;
  • une application d'une fonte considérée "universelle" via font-family: FreeSans, Arimo, "Droid Sans", Helvetica, Arial, sans-serif;

Sur chacun des enfants :

  • display: inline-block (forcément)
  • une remise à zéro des valeurs du parent : letter-spacing: normal; word-spacing: normal; text-rendering: auto;
  • le rétablissement de la fonte souhaitée, par exemple font-family: TimesNewRoman, "Times New Roman", Times, Baskerville, Georgia, serif;

Grâce à ce cumul d'astuces, nous obtenons le résultat souhaité de manière quasi universelle sur l'ensemble des navigateurs.

Remarque : PureCSS applique une couche additionnelle à ses grilles, il s'agit du positionnement Flexbox que PureCSS applique aux navigateurs modernes.

Démonstration

La technique "display: table" de Cahnory

Un développeur web français, Cahnory, a découvert une solution beaucoup plus simple que toutes les autres. En tout cas sur le papier.

Globalement l'idée consiste à profiter du comportement naturel d'un tableau, à savoir qu'il ne peut pas y avoir d'espace entre les cellules, par défaut.

En clair, il suffit tout simplement d'appliquer  display: table; sur le parent !

Sur le principe, c'est parfait, il est cependant nécessaire d'ajouter quelques petites précautions d'usage sur le parent pour assurer une largeur globale et se prémunir de tout agrandissement dû aux contenus : table-layout: fixed; et width: 100%.

Malheureusement, en pratique, cette solution se heurte à quelques navigateurs défaillants, en l'occurence Internet Explorer et Firefox chez lesquels l'espace indésirable demeure. Il sera nécessaire d'appliquer un word-spacing: -2em; sur le parent et un word-spacing: normal; sur chaque enfant pour supprimer cet espace.

Au final et avec l'ensemble des précautions, le code final sera :

.parent {
  display: table; 
  width: 100%; 
  table-layout: fixed; 
  word-spacing: -2em;
}
.enfant {
  display: inline-block; 
  word-spacing: normal;
}

Démonstration

Conclusion

Ce petit et rapide tour d'horizon des différentes techniques de contournement de ce fâcheux espace indésirable introduit par la propriété inline-block nous apprend en définitive que nous n'en sommes encore qu'à un stade de l'expérimentation et du tâtonnement.

En attendant la solution ultime émanant de CSS3 mais encore non supportée, l'ensemble des techniques CSS repose actuellement sur des hypothèses et sont fortement dépendantes de contextes précis : rien de vous assure que ces bidouilles fonctionneront partout selon les configurations de vos visiteurs. C'est donc à vous de les tester au cas par cas.

La meilleure solution, si vous en avez la possibilité, demeure finalement de supprimer physiquement l'espace au sein du HTML.

Voir toutes les techniques

Commentaires

Quelles versions de Safari sont concernées par les font-size:0 ignorés? Ça ne semble pas être le cas dans Safari 5.1.3 sous OSX.

Par ailleurs, à partir du moment où on rétablit la taille de texte en utilisant `rem`, je pense qu’il vaut mieux mettre la taille de texte à zéro en utilisant cette unité aussi: `font-size: 0rem`. Ainsi, dans IE 7-8 on ne supprime pas l’espace entre les blocs mais au moins on garde un contenu lisible.

Coller les éléments les uns aux autres dans le code HTML est encore la meilleure solution, dans certains cas, ça m'a sauvé la mise dans certaines intégrations un peu poussées où je ne pouvais rien faire d'autre.

Le cas typique : des liens qui se suivent, comme le display:inline-block; marche bien sur des éléments qui sont de type inline (merci IE...), ça permet de s'en sortir à moindre frais si j'ose dire.

@fvsch : ah alors c'est peut-être réservé à Safari Windows, ce qui change un peu la donne.
Sinon, oui pourquoi pas 0rem / 2rem. J'y médite... ça corrigerait le problème sur IE6/IE7 (qui applique inline et non inline-block), mais pose un souci sur IE8 (qui applique inline-block et ne comprend pas rem)

Expérience à rapporter : la technique du font-size:0; sur le parent ne fonctionne pas sous androïd browser

Vous n'avez pas abordé la propriété word-spacing. Même si celle-ci ne résout pas entièrement le problème, elle peut s’avérer utile à la gestion de ces espaces.

@ryuran : merci pour le retour pour font-size: 0. Pourriez-vous être plus précis car de mon côté cela fonctionne parfaitement sur Galaxy Tab Android 3.2 (navigateur par défaut + firefox + opera)

Pour word-spacing, avez-vous fait des tests ?
Je viens de tester et c'est plutôt positif, ça permet de résoudre le problème de compatibilité Opera, merci :)

font-size: 0; fonctionne chez moi sous HTC desire HD / Android 2.3.5 / FF10.0.3 mais pas sous le navigateur par défaut.

Au passage, merci pour les astuces, j'applique principalement la marge négative, mais c'est vraiment de la bidouille...

HTC sense sous androïd 2.3.4
HTC hero sous androïd 2.1

Sur ces 2 versions le font-size:0; laisse quand même un espace. Il n'y a eu que la suppression des espaces entre les balises ou les commentaires d'efficaces.

Il y a quelques jours j'ai découvert le même phénomène, un espace de 4px entre deux input ... (formulaire recherche: champ de saisie + bouton ok)
Mais bon dans le web je suis comme un nouveau-né qui cherche à faire ses premiers pas alors je découvre ... et je trouve que la solution des commentaires HTML tombe TipTop ...

C'est vrai que même si cela semble contraire aux bonnes pratiques, mettre en forme le contenu en modifiant le code HTML semble être la solution la plus simple à mettre en place et aussi la plus sûre.
Et puis le détournement des commentaires permet quand même de conserver la mise en forme du code pour une bonne lisibilité.
Pour moi c'est tout vu ! :)

Pour ce cas précis que j'ai rencontré de nombreuses fois dans mes projets, je dispose simplement mes blocs en "float:left". Comme ca, pas de problème de white-space. Tout est calé au pixel près.

Sinon il y a aussi la possibilité de faire un léger script en Javascript, pour les replacer. Mais du coup on utilise plus "inline-block" mais plutôt un "position : absolute" pour les enfants et un "relative" pour le parent. Je reste quand même plutôt adepte de la solution html avec les commentaires.

Si on tente d'améliorer son score de PageSpeed, ces espaces ne posent plus de problème, une des recommandations étant de les supprimer et d'écrire la source (X)HTML sur une ligne.

Dans ma boite, on utilise pour ça un action filter d'ASP.net MVC, ça permet de préserver l'accessibilité et la lisibilité du code source mais de générer un contenu optimisé au niveau du poids. Grosso modo, chaque page, une fois générée côté serveur, passe dans un filtre qui s'occupe de faire le ménage. Seule limitation : tout espace qu'on souhaite conserver ne doit pas être directement adjacent à un tag... mais ça se vit très bien au quotidien !

Pour PHP, il me semble qu'il soit aussi possible de post-traiter le buffer avant son envoi au navigateur.

Avec twig comme moteur de template, il y a {%spaceless%} qui supprime tous les espaces entre les balises.
Pas besoin de tricher dans le CSS ou dans la structure HTML.
{%spaceless%}
<div>
<p>Lorem Elsass ipsum</p>
<p>réchime amet sed bissame so libero knackes choucroute…</p>
<p>Hopla kuglopf flammekueche</p>
</div>
{%endspaceless%}

et c'est tout :-)

Sinon pour ceux qui n'utilisent pas twig, avant de servir la page ou la zone qui pose problème il suffit de faire ça en PHP :
$data = "
<div>
<p>Lorem Elsass ipsum</p>
<p>réchime amet sed bissame so libero knackes choucroute…</p>
<p>Hopla kuglopf flammekueche</p>
</div>
"
trim(preg_replace('/>\s+</', '><', $data ));

Sinon avec ob_start() et ob_get_clean() :
<?php ob_start(); ?>
<div>
<p>Lorem Elsass ipsum</p>
<p>réchime amet sed bissame so libero knackes choucroute…</p>
<p>Hopla kuglopf flammekueche</p>
</div>
<?php trim(preg_replace('/>\s+</', '><', ob_get_clean() )); ?>

Un article qui tombe à point car je ferraillais avec justement et étant assez empirique comme garçon, j'en étais venu à une marge négative de 4px!

Je m'en vais donc corriger cela dans mon HTML!

Merci.

Pour infos dans le même esprit, on peut tout mettre sur la même ligne, on aura pas d<espace non plus :

<div></div><div></div>...

J'en conviens ce n'est pas pratique et l'utilisation du font-size: 0 est plus pertinente sur de gros et à long terme. Voila.
letter-spacing et word-spacing marchent assez bien mais il faut utiliser les deux pour couvrir la plupart des navigateurs, les inline-block ont l'air d'être pris poour des mots par certains et des lettres par d'autre.
Le désavantage c'est qu'il peut y avoir des effets de bord avec l'un et l'autre de ces fix contrairement au font-size: 0.
Par contre le support safari windows je laisserais tomber.

@Raphael : oui mais... est-ce qu'il y a un autre moyen que le float pour positionner un label à gauche d'un textarea de 10 lignes sans qu'il (le label) n'apparaisse tout en bas (au niveau de la «baseline» du textarea) ?
OK mon exemple est très spécifique mais s'il y a une technique plus propre avec le inline-block je suis preneur.