Position absolue et taille des éléments HTML

Travaillant principalement sur un produit permettant de construire des applications web, j'ai souvent dû expliquer à mes collègues issus du monde java le pourquoi du comment du positionnement et de la taille des éléments HTML en CSS. Ceci est un texte rédigé pour expliquer la chose...

Note : ce texte trainait dans mes brouillons depuis fin 2008 ; je l'ai mis en ligne en octobre 2010.

Introduction

Ce texte n'a absolument pas pour ambition de faire le tour de la question du positionnement des éléments HTML. Il traite essentiellement de deux points particuliers :

  • La relation entre le positionnement absolue et la taille d'un élément.
  • Le comportement de deux éléments - l'un enfant de l'autre - lorsqu'ils sont tout deux positionnés en absolue.

Pour illustrer le propos, nous prendrons une structure HTML simple :

<div class="parent">
    <div class="enfant">
    </div>
</div>
Code HTML à étudier.

Le but du jeu est de positionner l'élément enfant par rapport à parent. Pour visualiser les effets des styles dans ce document, nous incluront notre code dans un bloc conteneur cadre qui jouera le rôle du document dans lequel on souhaite placer notre code :

<!-- conteneur -->
<div class="cadre">
    <!-- code HTML à étudier -->
    <div class="parent">
        <div class="enfant">        
        </div>
    </div>
</div>
Structure du code HTML de la démonstration.

Nous donnerons à ce conteneur une hauteur de 100px et une couleur grise et, toujours pour faciliter la visualisation des éléments qui nous intéressent, nous donnerons à l'élément parent une bordure ainsi qu'une couleur de fond rouge et à l'élément enfant la même chose mais en vert. Enfin, pour ne pas fausser la démonstration, nous fixerons à 0 les marges internes (padding) et externes (margin) des éléments :

div {
    padding: 0;
    margin: 0;
}
.cadre {
    position: relative;
    height: 100px;
    background: #f5f5f5;
}
.parent {
    border: 1px solid #f00;
    background: #f00;
}
.enfant {
    border: 1px solid #0f0;
    background: #0f0;
}
Styles de bases des démonstrations

Ces styles, destinés à faciliter l'illustration du propos ne seront pas reprit par la suite.

Parsing d'un document HTML : le flux du document

Le moteur de rendu d'un navigateur web lit la page HTML qu'il reçoit dans le sens de lecture occidental : de gauche à droite et de haut vers le bas. Les éléments vont donc s'ajouter les uns à la suite des autres ou les uns dans les autres en fonction de l'arborescence du document. C'est cette agencement que l'on qualifie de « flux du document », ce qui signifie que sans application de styles particuliers, le rendu des éléments dépendra de leur contexte, donc des éléments qui les entoure dans ce flux.

Voyons d'abord à quoi ressemble le code HTML à étudier sans toucher au positionnement ou à la taille :

/// <div class="cadre"> <div class="parent"> <div class="enfant"> </div> </div> </div> ///

Rendu de base du code de démonstration.

Que voyons-nous ? rien, si ce n'est les bordures des éléments, tout simplement parce que enfant et parent n'ont pas de contenu à afficher. Ils prennent l'ensemble de la place disponible en largeur (l'élément cadre gris) car ce sont des éléments div dont l'affichage par défaut est de type « bloc »(1).

Insérons maintenant un contenu à enfant, un paragraphe de texte par exemple(2) :

<div class="parent">
    <div class="enfant">
        <p>Lorem ipsum.</p> 
    </div>
</div>
Insertion d'un contenu dans enfant.

/// <div class="cadre"> <div class="parent"> <div class="enfant"> <p>Lorem ipsum.</p> </div> </div> </div> ///

Rendu du code avec un contenu.

Nous voyons les bords de la structure s'écarter pour faire place à son contenu. Il s'agit là d'un comportement typique d'éléments présent dans le flux du document.

Nous pouvons modifier cette représentation en appliquant par exemple des tailles particulières aux éléments (pour les éléments de type bloc uniquement). Décidons donc que notre enfant doit avoir 30px de hauteur :

.enfant {
    height: 30px;
}
Styles pour appliquer une hauteur à l'élément enfant.

/// <div class="cadre"> <div class="parent"> <div class="enfant" style="height:30px"> <p>Lorem ipsum.</p> </div> </div> </div> ///

Rendu de la hauteur de l'élément enfant.

Que voyons-nous maintenant ? Que l'enfant a bien 30px de hauteur mais surtout que la taille du parent à grandit en conséquence (la bordure rouge a suivit le contenu vert), preuve que ces deux éléments appartiennent au flux du document et qu'ils sont interdépendants.

Notez cependant que ceci n'est vrai que pour la hauteur : la largeur du parent ne s'adaptera pas à celle de son enfant si celle-ci est inférieure à la « largeur naturelle bloc » de l'élément.

Sortir du flux : position absolue

Le flux du document commence donc en haut à gauche de la page et se poursuit comme pour la lecture humaine. Si les règles CSS nous permettent de jouer avec ce flux plus ou moins finement, comme nous en avons vu simple exemple ci-dessus, il est possible de s'en extraire pour ne plus avoir à suivre le sens de la page. C'est ce qu'on appelle « sortir un élément du flux » et cela arrive dés qu'il est placé en position absolue par exemple.

Position absolue de l'enfant

Sortons donc notre élément enfant du flux de la page :

.enfant {
    height: 30px;
    position: absolute;
}
Règles CSS pour sortir l'enfant du flux.

/// <div class="cadre"> <div class="parent"> <div class="enfant" style="height:30px; position: absolute;"> <p>Lorem ipsum.</p> </div> </div> </div> ///

Rendu de l'enfant lorsqu'il est sorti du flux.

Nous voyons que la largeur de l'enfant a changé : elle s'est maintenant réduite pour s'adapter à son contenu (3). Ensuite, la hauteur du parent ne suit plus celle de son enfant ; c'est comme s'il ne contenait plus rien. L'enfant est sortit du flux et ne force plus la forme de son parent.

Sortir un élément du flux permet avant tout (mais pas seulement) de le positionner plus finement. Ce positionnement s'effectue grâce à 4 propriétés aux noms suffisamment explicites, top, left, right et bottom, mais à quoi se référent ces coordonnées ? Elles ne peuvent plus désigner le parent de l'élément puisque, comme nous l'avons vu, un élément en position absolue est sortie du flux et n'a donc plus de relation directe de la sorte avec sont ascendant dans l'arborescence. En fait, ce n'est pas tout à fait vrai : les propriétés de coordonnées vont placer l'élément par rapport au premier de ses ascendants qui sera lui-même en position absolue ou relative. Si aucun de ses ascendants n'est positionné absolument (ou relativement), le positionnement se fera par rapport à la racine du document, à savoir l'élément html(4).

Décidons par exemple de placer l'enfant à 10px du bord haut et à 10px du bord gauche :

.enfant {
    height: 30px;
    position: absolute;
    top: 10px;
    left: 10px;
}
Règles CSS pour positionner en absolue l'enfant.

/// <div class="cadre"> <div class="parent"> <div class="enfant" style="height:30px; position: absolute; top:10px; left:10px;"> <p>Lorem ipsum.</p> </div> </div> </div> ///

Positionnement absolu de l'enfant.

Nous voyons donc ici que enfant se positionne par rapport à son plus proche parent lui-même positionné, à savoir cadre.

Position absolue du parent

Décidons maintenant que le parent aura aussi un position absolue, et plaçons-le à 10px du bord haut et 10px du bord gauche :

.parent {
    position: absolute;
    top: 10px;
    left: 10px;
}
.enfant {
    height: 30px;
    position: absolute;
    top: 10px;
    left: 10px;
}
Règles CSS pour positionner parent et enfant en absolue.

/// <div class="cadre"> <div class="parent" style="position: absolute; top: 10px; left: 10px;"> <div class="enfant" style="height:30px; position: absolute; top:10px; left:10px;"> <p>Lorem ipsum.</p> </div> </div> </div> ///

Positionnement absolu de l'enfant et du parent.

Plusieurs choses sont à retenir ici :

  1. D'abord, et comme on s'y attendait, notre enfant ne se place plus à 10px du bord haut de cadre mais à 10px du bord haut de parent, son plus proche ascendant en position absolue. Idem pour sa position par rapport au bord gauche.
  2. Ensuite, le parent positionné en absolue est maintenant réduit à un simple point (en fait, ses bordures) car, comme on la vu, un élément en position absolue voit ses tailles définies par son contenu, et ici parent ne contient rien.
  3. Enfin, on voit aussi que la largeur de enfant a changé : elle s'est réduite au maximum que pouvait lui permettre son contenu (la largeur du mot « lorem »).

Ce dernier point est a souligné car il peut parfois entraîner des bugs de rendu dont on ne comprend pas tout de suite l'origine et nous oblige à reformuler un peu le point 2. Imaginons que nous donnions une largeur précise à parent, disons 200px, quelles conséquences cela aura pour enfant ?

/// <div class="cadre"> <div class="parent" style="position:absolute; top:10px; left:10px; width: 200px;"> <div class="enfant" style="height:30px; position: absolute; top:10px; left:10px;"> <p>Lorem ipsum.</p> </div> </div> </div> ///

Parent et enfant en position absolue, et largeur définie pour parent.

Nous voyons qu'il retrouve son état « initial » car la largeur de parent le lui permet. Donc, la largeur d'un élément positionné est définie par son contenu dans le flux mais peut être limitée par celle de son ascendant positionné si celle-ci est inférieure à la « largeur naturelle » du contenu.

Taille et positionnement absolue

Poursuivons la reflexion sur la taille des éléments en position absolue. Nous avons vu que le positionnement se fait en utilisant deux des propriétés de positionnement, l'une pour le positionnement en abscisse, la seconde pour le positionnement en ordonnées, soit les couples left-top (en haut à gauche), left-bottom (en bas à gauche), right-top (en haut à droite) ou right-bottom (en bas à droite). Mais que se passe-t-il si l'on déclare en même temps les deux propriétés de positionnement en abscisse (left et right) ou les deux propriétés de positionnement en ordonnées (top et bottom) ?

Et bien l'élément sera positionné suivant les deux coordonnées « supérieures » (top et left), puis il sera étiré pour obéir aux deux coordonnées « inférieures » (bottom et right), ce qui lui donnera donc une largeur et une hauteur, ceci seulement si celles-ci ne sont pas explicitement définies ; si c'est le cas, les tailles définies par les propriétés width et height supplantent celles qui sont calculées en fonction des coordonnées « inférieures ».

Illustrons ce comportement en étirant l'élément parent pour que ses 4 côtés se retrouvent à 10px de cadre :

.parent {
    position: absolute;
    top: 10px;
    bottom: 10px;
    left: 10px;
    right: 10px;
}
.enfant {
    height: 30px;
    position: absolute;
    top: 10px;
    left: 10px;
}
Code CSS pour étirer parent.

/// <div class="cadre"> <div class="parent" style="position: absolute; top: 10px; bottom: 10px; left: 10px; right: 10px;"> <div class="enfant" style="height:30px; position: absolute; top:10px; left:10px;"> <p>Lorem ipsum.</p> </div> </div> </div> ///

Rendu de l'étirement du parent.

Nous voyons que parent s'est étendu pour obéir aux 4 propriétés de positionnement alors même qu'on ne lui a pas défini de hauteur ou de largeur. Essayons de faire la même chose avec enfant :

.parent {
    position: absolute;
    top: 10px;
    bottom: 10px;
    left: 10px;
    right: 10px;
}
.enfant {
    height: 30px;
    position: absolute;
    top: 10px;
    bottom: 10px;
    left: 10px;
    right: 10px;
}
Code CSS pour étirer parent et enfant.

/// <div class="cadre"> <div class="parent" style="position: absolute; top: 10px; bottom: 10px; left: 10px; right: 10px;"> <div class="enfant" style="height:30px; position: absolute; top:10px; bottom:10px; left:10px; right:10px;"> <p>Lorem ipsum.</p> </div> </div> </div> ///

Rendu de l'étirement du parent et de l'enfant.

Que voyons-nous ? L'enfant s'est bien étendue pour obéir aux propriétés left et right : il est maintenant à 10px du bord gauche et à 10px du bord droit de parent ; Il se positionne aussi correctement en suivant la propriété top ; Par contre, il ne tient absolument pas compte de la propriété bottom, tout simplement par ce qu'on lui applique une hauteur (height) et que cette propriété supplante le comportement d'étirement.

Résumé

Que retenir de ces petites manipulations sur le positionnement et la taille des éléments ?

  1. Pour un élément de type bloc dans le flux du document :
    • Il prend la largeur que lui permet son parent et la hauteur définie par son contenu, sauf si celles-ci sont explicitement définies par les propriétés width et height.
    • La hauteur du parent est induite par celle de son enfant.
  2. Pour un élément en position absolue :
    • Il se positionne par rapport à son plus proche ascendant positionné en absolue (ou en relatif).
    • Il prend une largeur et une hauteur définies par son contenu présent dans le flux, sauf si celles-ci sont explicitement définies par les propriétés width et height. Cette largeur pourra être réduite par celle de son ascendant positionné si celle-ci est inférieure à la « largeur naturelle » du contenu.

Références

chapter
9. Visual formatting model
chapter
10. Visual formatting model details

Notes

1

qui signifie, pour faire simple, que l'élément prend toute la largeur disponible et adapte sa hauteur en fonction de son contenu.

2

Nous supprimons aussi les marges internes et externes de cet élément.

3

Un phénomène qui déborde le sujet de ce texte, en relation avec le type des éléments et leur rendu (display) par défaut dans les navigateurs.

4

La taille de celui-ci se confond souvent avec celle de la fenêtre du navigateur, mais il ne s'agit pas d'une règle stricte : on peut très bien appliquer des styles à l'élément html comme sur tout autre élément ; l'unique limitation viendra de la mauvaise gestion de ces styles par IE 6.