I. Le problème▲
Dans le code HTML suivant, vous avez trois balises <div> chacune contenant une balise <span>. Chaque <span> possède une couleur de fond, respectivement rouge, vert et bleu. Ces <span> sont aussi en position absolute vers le bord haut et gauche du document et se chevauchent légèrement, vous pouvez ainsi voir lesquels sont au-dessus des autres. Le premier <span> à un z-index valant 1, les autres n'en ont pas encore.
Voici les codes HTML et CSS pour ces éléments, que vous pouvez visualiser (avec Codepen) avec le CSS complet :
<div>
<span class
=
"red"
>
Red</span>
</div>
<div>
<span class
=
"green"
>
Green</span>
</div>
<div>
<span class
=
"blue"
>
Blue</span>
</div>
.red
,
.green
,
.blue
{
position:
absolute
;
}
.red
{
background:
red
;
z-index:
1
;
}
.green
{
background:
green
;
}
.blue
{
background:
blue
;
}
See the Pen Stacking Order (problem) by Philip Walton (@philipwalton) on CodePen
Votre défi :
essayez de faire passer le <span> rouge en dessous des <span> bleu et vert en respectant les règles suivantes :
- vous ne devez pas modifier le code HTML,
- vous ne pouvez modifier la propriété z-index d'aucun élément,
- vous ne pouvez modifier la propriété position d'aucun élément.
Pour effectuer vos essais, cliquez sur le lien « edit on Codepen » de l'exemple ci-dessus. Le résultat doit ressembler à l'exemple ci-dessous.
Attention, ne cliquez pas sur l'onglet CSS du résultat ou vous verrez immédiatement la solution.
See the Pen Stacking Order (solution) by Philip Walton (@philipwalton) on CodePen
II. La solution▲
La solution consiste à affecter une opacité inférieure à 1 pour la première <div> (l'élément parent du <span> rouge). Voici le CSS qui a été ajouté dans le code du résultat :
div:
first-child
{
opacity:
.99;
}
Si vous vous arrachez les cheveux en refusant de croire que l'opacité joue un rôle pour déterminer l'ordre d'empilement des éléments, alors bienvenu au club ! J'ai eu la même réaction lorsque j'ai constaté cela la première fois.
Heureusement, la suite de cet article devrait vous aider à mieux comprendre le pourquoi du comment.
III. L'ordre d'empilement▲
La propriété z-index semble pourtant si simple à comprendre : les éléments avec un z-index plus élevé sont placés au-dessus de ceux ayant une plus petite valeur, c'est bien ça ? En fait, pas exactement et c'est bien le problème avec z-index. Son fonctionnement semble si simple que la plupart des développeurs ne prennent pas le temps de lire les spécifications.
Lorsque ni z-index ni position ne sont définis, la règle est simple : l'ordre d'empilement des éléments correspond à leur ordre d'apparition dans le code HTML. En fait, c'est un peu plus compliqué que cela, mais tant que vous n'utilisez pas de marges négatives pour le chevauchement d'éléments de type inline, vous ne rencontrerez probablement pas les effets de bord.
Lorsque vous utilisez la propriété position, tout élément positionné (et ses enfants) sera affiché au-dessus de tout élément non positionné. Pour rappel, un élément est dit positionné si sa propriété position vaut autre chose que static (la valeur par défaut), c'est-à-dire relative, absolute ou fixed.
Enfin, lorsque vous utilisez la propriété z-index, les choses se compliquent un peu. Au premier abord, il paraît naturel de considérer que les éléments ayant un z-index plus élevé se placent au-dessus de ceux en ayant un moins élevé et que les éléments ayant un z-index se placent au-dessus de ceux qui n'en ont pas, mais ce n'est pas aussi simple. Tout d'abord, z-index ne fonctionne qu'avec des éléments positionnés. Si vous essayez d'affecter un z-index à un élément non positionné, cela n'aura aucun effet. Ensuite, z-index peut créer des contextes d'empilement et soudainement, ce qui semblait simple devient beaucoup plus compliqué.
IV. Le contexte d'empilement▲
Des groupes d'éléments ayant un parent commun qui se déplace en avant ou en arrière dans l'ordre d'empilement créent ce que l'on appelle un contexte d'empilement (stacking context). Bien comprendre la notion de contexte d'empilement est la clé pour maîtriser parfaitement le fonctionnement de z-index et de l'ordre d'empilement.
Chaque contexte d'empilement possède un unique élément racine. Lorsqu'un contexte d'empilement est créé sur un élément, tous ses éléments enfants sont alors confinés à un emplacement défini (celui de l'élément racine) de l'ordre d'empilement. Cela signifie que si un élément se situe dans un contexte d'empilement tout au fond de la pile d'affichage, il ne pourra jamais se placer au-dessus d'un élément d'un autre contexte se situant au-dessus dans l'ordre d'empilement, même en lui affectant un z-index dépassant le milliard !
Il y a trois façons de créer un contexte d'empilement :
- lorsqu'un élément est l'élément racine du document (la balise <html>) ;
- lorsqu'un élément a une propriété position différente de static et un z-index différent de auto ;
- lorsqu'un élément a une opacité inférieure à 1.
Les deux premiers points semblent logiques et sont habituellement bien compris des développeurs Web (même s'ils ne connaissent pas la notion de contexte d'empilement).
Le troisième point (l'opacité) n'est quasiment jamais évoqué en dehors des spécifications du W3C.
V. Déterminer la position d'un élément dans la pile▲
En fait, déterminer l'ordre d'empilement complet pour tous les éléments de la page (en incluant les bordures, arrière-plans, nœuds textes, etc.) est extrêmement compliqué et va très au-delà du cadre de cet article (encore une fois, je vous renvoie aux spécifications).
Cependant, dans la plupart des cas, une compréhension simplifiée peut mener suffisamment loin et aider à rendre le développement CSS prévisible. Essayons donc de suivre la logique d'ordonnancement pour un contexte d'empilement.
V-A. Ordre d'empilement dans un même contexte▲
Voici les règles principales pour déterminer l'ordre d'empilement au sein d'un même contexte (du bas vers le haut).
- L'élément racine du contexte.
- Les éléments positionnés (et leurs enfants) avec des valeurs de z-index négatives (les valeurs les plus hautes étant placées au-dessus des valeurs les plus basses ; les valeurs égales sont placées selon leur ordre d'apparition dans le code HTML).
- Les éléments non positionnés (placés selon l'ordre d'apparition dans le code HTML).
- Les éléments positionnés (et leurs enfants) ayant un z-index valant auto (placés selon leur ordre d'apparition dans le code HTML).
- Les éléments positionnés (et leurs enfants) avec des valeurs de z-index positives (les valeurs les plus hautes étant placées au-dessus des valeurs les plus basses ; les valeurs égales sont placées selon leur ordre d'apparition dans le code HTML).
Note : les éléments positionnés ayant des valeurs de z-index négatives sont placés en premier dans la pile du contexte, ce qui signifie qu'ils apparaissent en dessous des autres éléments. À cause de cela, il est possible pour un élément d'être en dessous de son propre parent, ce qui est normalement impossible. Cela n'est toutefois possible que si le parent en question se trouve dans le même contexte et qu'il n'est pas l'élément racine de ce contexte. Vous pouvez voir une magnifique illustration de cela dans l'article de Nicolas Gallagher CSS drop-shadows without images.
V-B. Ordre d'empilement global▲
En comprenant bien quand et comment est créé un nouveau contexte d'empilement, ainsi que la façon dont est ordonnée la pile au sein d'un contexte, vous devriez vous en sortir pour prévoir où se situeront les éléments.
Le point-clé pour pouvoir s'y retrouver est d'être capable de détecter chaque nouvelle formation de contexte d'empilement. Si vous affectez un z-index d'un milliard et qu'il n'apparaît pas en haut de la pile, inspectez ses éléments ancêtres pour vérifier si l'un d'eux ne crée pas un nouveau contexte. Si c'est le cas, alors aucune valeur de z-index ne pourra vous aider.
VI. Conclusion▲
Pour revenir au problème exposé au début de l'article, j'ai réécrit la structure HTML avec en commentaire de chaque balise sa position dans la pile. Cet ordre correspond au code CSS initial.
<div><!-- 1 -->
<span class
=
"red"
>
<!-- 6 -->
</span>
</div>
<div><!-- 2 -->
<span class
=
"green"
>
<!-- 4 -->
<span>
</div>
<div><!-- 3 -->
<span class
=
"blue"
>
<!-- 5 -->
</span>
</div>
Lorsque nous ajoutons une opacité à la première <div>, l'ordre devient :
<div><!-- 1 -->
<span class
=
"red"
>
<!-- 1.1 -->
</span>
</div>
<div><!-- 2 -->
<span class
=
"green"
>
<!-- 4 -->
<span>
</div>
<div><!-- 3 -->
<span class
=
"blue"
>
<!-- 5 -->
</span>
</div>
span.red est passé de 6 à 1.1. J'ai utilisé un point pour indiquer qu'un nouveau contexte était créé et span.red est le premier élément de ce contexte.
J'espère que vous comprenez mieux pourquoi le carré rouge est passé en dessous des autres éléments. Le premier exemple comprenait seulement deux contextes : la racine du document et celui créé par span.red. En ajoutant une opacité au parent de span.red, nous avons formé un troisième contexte, ainsi le z-index défini sur span.red ne s'applique qu'à l'intérieur de ce contexte. Comme la première <div> et ses éléments frères ne sont pas positionnés et n'ont pas de z-index, leur ordre d'empilement correspond à leur ordre d'apparition dans le code HTML, ce qui signifie que la première <div>, ainsi que tous les éléments inclus dans son contexte d'empilement, sont affichés en dessous des <div> suivantes.
Compléments et remerciements▲
- Elaborate description of Stacking Contexts.
- The stacking context.
- The Z-Index CSS Property: A Comprehensive Look.
Cet article a été publié avec l'aimable autorisation de Philip Walton. L'article original (What No One Told You About Z-Index) peut être vu sur le blog de l'auteur.
Nous tenons à remercier zoom61 et Claude Leloup pour leur relecture attentive de cet article.