Positionnement d'une page html par css
===========================================
Ce document résume quelques notions pour le positionnement des objets
html à l'aide d'un fichier css.
En théorie, il y a une documentation complète en ligne. En pratique,
c'est souvent assez pénible car la documentation est souvent évasive.
On peste en se demandant ou est décrit le fichu algorithme utilisé.
Cher lecteur, ce document est pour toi si comme moi tu as
peiné plusieurs jours avec des objets dont le positionnement
saute de façon erratique quand tu as changé un
paramètre, si tu cherches à connaître
l'algorithme de positionnement utilisé par le navigateur.
Le type d'objets a placer
---------------------------
Il y a plusieurs types d'images qui apparaissent dans une page web,
au sens ou les règles de postionnement qu'on leur applique sont
différentes :
- les images qui fixent le cadre et sont fixes indépendamment des
modifications ultérieures du contenu
- les images qui s'inscrivent dans un flot texte-image et sont placées
au gré du texte.
- les elements décoratifs attachés a un contenu ( exemple: un logo de
crayon en haut de chaque article )
Il y a aussi plusieurs types de positionnement pour un texte:
- le texte fixe ( Titre d'Accueil de la page web, menus...)
- le texte en flot, en général actualise' au fur et à mesure des
ajouts du site
Il y a enfin
- Des textes et images qui restent toujours a la meme place sur l'ecran
pour rester visibles ( par exemple un menu qui reste visible meme si
on bouge l'ascenceur).
Il faut mettre les objets précédents en relation avec les options de positionnement
adéquates de css.
De la racine vers les feuilles, avec les marqueurs
--------------------------------------------------
L'algorithme de positionnement utilisé par le navigateur qui va
afficher la page web est le suivant. Le navigateur construit
l'arbre d'objets de la page. Il positionne les objets en commencant
par la racine et en finissant aux feuilles, en explorant toute la
branche en profondeur avant de passer à la branche voisine.
Lors de cette exploration de l'arbre, le positionnement de chaque
objet rencontré dépend du marqueur de position associé à l'objet.
Chaque objet est muni d'un
marqueur de position sous la forme d'un attribut
*position:valeur*, où valeur est choisie parmi les quatre
possibilités suivantes:
- *static* ( = le défaut)
- *absolute*,
- *relative*,
- *fixed*.
Les valeurs *initial* et *inherit* sont également possibles, mais elles
redonnent après calcul l'une des quatre possibilités précédentes.
Le positionnement des *static*
-------------------------------
Si le navigateur doit placer un objet "static", il va essayer de le
poser en n'empiétant pas sur les autres objets *static* et *relative* déjà posés.
En revanche, le navigateur ne se préocuppe pas du chevauchement des
objets *static* avec les objets *absolute* ou *fixed*.
Autrement dit, les objets absolus ou fixed sont "inconnus/transparents" pour les objets
static. Le navigateur commence par calculer la taille de l'objet
*static* qu'il cherche à poser. Il ajoute à cette taille les valeurs
des paramètres *padding* et *border-width* (si *border-style* n'est pas
positionné à *none*).
Cet objet dont la taille est maintenant connu doit maintenant être
posé dans une boite contenante. Cette boite contenante est
est déterminée: c'est un objet *div*, et parmi tous les *div* de
l'arbre, on choisit l'ancêtre le plus proche du *static* qu'on veut
poser. En l'absence d'ancêtre, c'est le document qui sert de div par
défaut.
La boite conteneur maintenant déterminée contient déja des objets
*static* et *relative*. Ce
sont des objets qui sont plus proches de la racine
et qui ont dejà été traités par le navigateur dans les étapes
précédentes de l'algorithme.
Ce peuplement laisse 2 empreintes
dans la boite. La petite empreinte e est formée par les objets déposés
dans la boite. La grande empreinte E est formée par les objets déposés
*aggrandis de leurs marges*.
L'objet static qu'on veut poser admet de meme deux empreintes o
et O, petites et grande, sans ou avec les marges. L'objet nouvellement posé devra
satisfaire les 2 conditions suivantes: o et E ne se rencontrent pas, O
et e ne se rencontrent pas. En d'autres termes, aucun objet ne doit
chevaucher un autre objet ni sa marge.
Parmi toutes les possibilités
de placement respectant ce critère, l'algorithme procède pour les
objets qui ont la propriété *display:inline* de la même
facon que lorsqu'on pose une nouvelle lettre sur une feuille de papier
quand on est en train d'écrire. On cherche une place libre de la
gauche vers la droite,
avec passage
a la ligne et retour à la gauche de la boite quand la ligne est
pleine.
Pour un objet
ayant la propriété *display:block* au lieu de *display:inline*
un retour à la ligne est imposé avant de rechercher une place libre
pour poser l'objet, à la manière d'une lettre sur une feuille de
papier quand on commence un nouveau paragraphe. Par exemple, les
eléments *
* d'une liste se voient attribuer par défaut *display:block*.
En conséquence, chaque nouvel élément d'une liste engendre automatiquement un passage à la
ligne. On peut changer la propriété en *display:block* si on a
besoin de présenter une liste horizontalement dans un menu sans retour
à la ligne.
La propriété *display:inline-block* permet de placer les
éléments comme avec *display:inline* tout en conservant la structure
de bloc. Concrètement, avec *display:inline-block*, les propriétés *width:value* et
*height:value* sont prises en compte, alors qu'elles sont sans effet
pour les objets *display:inline*. En termes intuitifs, cela signifie
qu'un élement *inline* est applati pour entrer comme dans un
interligne, tandis qu'un element *inline-block* est positionné comme
une lettre usuelle sans passage à la ligne, mais conserve sa géométrie
de bloc.
Si l'objet a poser a l'attribut *float:none*, le navigateur
pose le nouvel objet dès qu'il trouve une place libre comme décrit
précédemment.
Si *float=left* ou *float=right*, la position ainsi
trouvée par l'étape précédente n'est qu'une position intermédiaire.
On poursuit avec un decalage maximal vers la gauche ou vers la droite si
un tel decalage est possible.
En termes techniques et inambigus. ce procédé se décrit ainsi pour
placer un objet de propriétés *position:static*, *display:inline*.
On note (a,b)=(coordonnee ligne,coordonnee colonne)
les coordonnees du coin haut gauche
du dernier objet non flottant pose' dans la div ( a,b=0,0 si on
n'a pas encore posé d'objet). Le coin haut gauche du nouvel objet sera place' plus bas,
c'est a dire en (c,d) avec c>=a. On choisit c minimum avec c>=a de
sorte que la place libre sur la ligne de hauteur c soit assez grande
pour poser l'objet. Si float = left, on choisit d minimal. Si
float=right, on choisit d maximal. Si float=none et c=a, on choisit d
minimal parmi les d>=b. Si float=none et c>a, on choisit d minimal
parmi les d>=0.
En résumé, le positionnement des objets ayant *position:static* dépend
du contenant et des propriétés de l'objet lui même (*float* ou pas,
valeur de la propriété *display*...) en suivant globalement la
paradigme des lettres qu'on écrit sur une feuille. Il est possible
aussi d'utiliser d'autres paradigmes pour positionner des *static*
dans un *div*, notamment en affectant la propriété *display:flex*
au *div* conteneur. Je ne développe pas, mais ca peut être intéressant
à explorer si le paradigme de l'ecriture se révèle inadapté, par
exemple quand on
veut poser les objets dans la *div* en écrivant de bas en haut ou de
droite à gauche, ou que l'on veut mettre des espaces entre des mots
pour remplir la ligne entièrement.
Le positionnement des objets *positionnés*
------------------------------------------
On dit par definition qu'un élement qui n'a
pas la position "static" qu'il est "positionné".
Nous avons décrit le positionnement des objets *static*. Il reste à
voir le positionnement des objets positionnés. Les ojbets positionnés
sont munis d'attributs *top:value*, *right:value*, *left:value*,
*bottom:value* qui permettent d'affiner le placement.
Ces paramètres de
positionnement *top,right,left,bottom* sont ignorés dans le cas d'un
objet *static*.
Pour un objet dont le positionnement est *position:relative*,
le navigateur calcule d'abord le positionnement
comme dans le cas *position:static*.
Puis l'objet est décalé de la valeur des parametres
de positionnement *left,right, bottom, top*
Si un objet a position:absolute, la boite contenante n'est pas
la même que dans les cas précédents. Le navigateur commence par
rechercher le dernier *div* ancestral qui est un objet positionné.
Si tous les *div* qui sont des aieux
ont une position *position:static*, le div par défaut est la fenêtre
d'affichage du
navigateur (En particulier, dans ce cas, le redimensionnement du
document ne changera pas la place de l'objet !).
La boite ainsi définie sert de boite contenante
et l'objet *absolute* sera placé dans cette boite contenante à l'aide
des paramètres de position. Par exemple, si les paramètres
*left,bottom* sont déclarés, la mesure se fait à partir de la gauche
et du bas de cette boite contenante.
Le positionnement dit *absolute* n'est donc pas absolu. Il est relatif
à la boite contenante correspondant à la dernière boite ancestrale
positionnée. Ce n'est que si cette boite ancestrale est absente que le
positionnement est absolu par rapport à la fenêtre du navigateur.
Si un objet est positionné par *position:fixed*, les paramètres de positionnement son
interprétés par rapport à la fenetre du navigateur (=mode
sticky). C'est donc comme le mode *position:absolute* dans le cas ou
l'objet absolu n'a pas de boite ancestrale positionnee.
Chevauchements
---------------
Pour régler les priorités lors des chevauchement des objets qui
partagent une partie de l'écran, on dispose de la variable z-index.
Choix de positionnement pour les objets
---------------------------------------
Voici quelques possibilités pour mettre en oeuvre les elements css
précédents en fonction des types d'objets rencontrés.
- les objets "sticky" auront *position:fixed*. Exemple: un menu
toujours visible, même quand on bouge l'ascenseur pour faire défiler
la page. Les parents eventuels et le contexte n'ont pas d'influence.
- Les objets fixes dans le document auront
*position:absolute*. Ils sont soit orphelins, soit ont des
parents qui sont eux-meme dans une position qui n'est pas
susceptible de bouger.
- Les objets qu'on souhaite localiser dans une *zone d'accueil* de la
page et qui ont le droit de bouger dans cette zone d'accueil.
On fait un *div* de la taille adéquate pour decrire la zone
d'accueil et on décrit les objets de la zone d'accueil comme des
descendants de ce *div*. On peut déclarer ces objets en static.
Si les marges ou autres petits problèmes induisent
un placement difficile, on peut utiliser *position:relative* pour
ajouter des petits decalages.
- Les objets décoratifs qui suivent un contenu variable. Exemple: une plume
d'écrivain comme logo a droite de chaque article de longueur
variable. L'article est inclus dans un *div*.
L'élément décoratif (ici la plume) est inclus dans le même *div* avec
*position:absolute* ( exemple pour la plume: right:0px top:50% ).
Le *div* parent sera positionné ( c'est à dire non *static*) puisque
les absolute choisissent leur boite contenant seulement parmi
ascendants positionnés. Au besoin, on remplacera le
positionnement *static* du *div* parent par un *position:relative*
en laissant les paramètres top/left... à zéro.
- Les objets alignés ou plus généralement
en position précise les uns par rapport aux
autres doivent tous
être en position absolue et avoir le même ancêtre positionné (cet
ancetre étant eventuellement non existant).
Choix des unite's
------------------
Je préfère mettre des unite's fixes plutot que des
pourcentages. Les unités fixes se comportent bien quand on zoome ou
dézoome sur une page. Les alignements son respectés. Si on
mélange pourcentage et des donnees fixes, ça met parfois la pagaille,
notamment pour des photos qui sont parfois exprimees en pourcentage de leur taille
initiale. Les pourcentages restent indispensables quand on
veut mettre une image au milieu d'une colonne dont on ne connait pas
la longueur par exemple.
Quelques details utiles
-------------------------
Ne pas oublier de mettre *height:auto* aux *div* si on souhaite
un redimensionnement convenable pour contenir les dessins (*static*
et *relative*, non flottants) qui y sont inclus.
S'il reste des problemes, ajuster avec la commande overflow.