Accueil Blog ⇉ HTML : Aligner le dernier élément d'une liste

html

css

jquery

illustration_article

Coup de coeur tiré de SOF* concernant un problème récurrent en CSS, l'alignement des listes, et en particulier, l'alignement du dernier élément (à gauche) dans un container dont on ne connait pas forcément les dimensions, élément qui a tendance à se balader un peu n'importe où, sauf là où on voudrait qu'il soit, sauf à se lancer dans des calculs, sauf à..., bref, LE pénible.

Le dernier élément, pas de quoi en faire un film


A l'intention de ceux qui auraient zappé ce coup de coeur et pour bien en mesurer les avantages, voilà deux possibilités connues ; je précise l'objectif : à gauche, ce que l'on vise ; à droite ce qu'on obtient avec un simple flex/margin:auto ou inline-block/text-align:center.





Je vous fais cadeau des bidouillages habituels, et des float divers et variés, commençons par le basique de chez basique que nous allons modifier ; sans surprise, brut de chez brut, c'est moche :

<!--
#container{width:398px;height: 150px;border: 1px #808201 solid;background: #D7D88D;}
.child{width:78px;height:40px;border: red 1px solid;background: #DFA7A7;}
-->


<div id="container">
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
</div>




Attaquons tout d'abord à la main, histoire de débroussailler cette jungle.
On va mettre un display:inline-block sur les div enfants de façon à obtenir cet alignement ; ça marche mais l'alignement laisse à désirer, et sans connaître au préalable nos dimensions, ça ne sera jamais centré. (ex: si on rajoute un text-align:center, ça ruine tout notre travail.)



Avec flex et justify-content:space-around (ou autre valeur désirée) le début de la magie opère, c'est centré, mais la ou les dernières div se retrouvent au milieu, comme avec le text-align:center du dessus, c'est là où nous faisons intervenir le "coup de coeur" : en rajoutant à la suite des div présentes (les .child) autant de div qu'espérées en première ligne maximum, en leur donnant la même largeur et une hauteur à 0 => la hauteur à zéro est capitale.

help “Répétons-le : hauteur à 0”


On peut même bidouiller avec les marges, l'important étant que les div rajoutées aient la même largeur, + bords+marges, que les div "normales", comme ceci:

#container{width:378px;height: 150px;border: 1px #808201 solid;background: #D7D88D;overflow:hidden;display:flex;flex-wrap:wrap; justify-content:space-around;}
.child{width:78px;height:40px;border: red 1px solid;background: #DFA7A7;margin-top:20px; }


<div id="container">
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div style="width: 80px;height:0;"></div>
<div style="width: 80px;height:0;"></div>
<div style="width: 80px;height:0;"></div>
<div style="width: 80px;height:0;"></div>

</div>




On remarque que, quel que soit le nombre de div alignées sur le premier rang, ça marche très bien pour 5, 4, 3 etc...
Ce qu'on peut déjà en conclure, c'est que si vous êtes sûr que jamais (au grand jamais) votre container ne dépassera une valeur donnée (et donc connue), vous pouvez vous arrêtez là : le nombre attendu de div en ligne est également connu de fait.
Le problème se pose quand la largeur du container devient plus importante que la largeur cumulée des n div + 1 largeur de div (et que vous ne voulez pas centrer à gauche) les div "fantômes" remontant en première ligne dès que la place devient suffisante et de fait, la réalignant avec elles, donc mal, pour ne pas dire : n'importe comment. (En fait, l'alignement est très propre, mais ce qu'on voit est moche.)

Ce cas, ennuyeux certes, ne peut se produire que lorsqu'on laisse le container sans dimension maximale. Pour envisager un remède, il va falloir bricoler un peu, et tant qu'à bricoler, essayons d'améliorer la chose :
- En évitant de surcharger le code en dur avec des valeurs qu'on ignore.
- En positionnant les .child sans connaître la dimension initiale du container
- En automatisant le placement des div en fonction de la taille (ici en la modifiant, plus grande ou plus petite, nos div doivent toujours être alignées) ; voici d'ores et déjà le résultat, on remarque (de façon réjouie) que les div restent alignées même pendant les modifications de taille.



Et voici les codes :


#container{width:378px;padding:10px;height: 150px;border: 1px #808201 solid;background: #D7D88D;overflow:hidden;display:flex;flex-wrap:wrap; justify-content:space-around;}
.child{width:78px;height:40px;border: red 1px solid;background: #DFA7A7;margin:5px; }
.new{width:80px;height:0;margin:5px;}



<div id="container">
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
<div class="child"></div>
</div>


J'ai modifié le style pour aérer un brin, en ajoutant un padding au container et des margin aux enfants (y compris aux div fantômes), et le HTML ne comprend que les div requises. (Au passage, leur nombre n'a aucune importance, on va gérer ça en-dessous)
et le JQuery :


var le = $(".child").length; // Nombre de .child
var childWi = $(".child").width() + 10; // largeur d'une .child (hors-tout hein !)
var parWi = $("#container").width() + 2; // largeur du container (toujours hors-tout)
var maxLign = parseInt(parWi / childWi); // Nbr de .child sur une ligne (parseInt pour récupérer un int)
Ajout(); // on initialise le nombre de div fantômes
$("#container").resizable({ //pour les besoins de cet article, resizable
resize: function(e,evS) { // récupe des modifications de taille "on the fly" **
Resiz(evS.size.width); // on expédie la nouvelle taille à Resiz()
}
});

function Ajout(){
if(!$(".new")[0]) { // on vérifie qu'il n'y a pas déjà des .new (sinon il va en mettre un paquet avec Resiz()
for(var i=0; i<maxLign;i++){ // et tant que i augmente vers le nombre de divs max en une ligne
$("#container").append('<div class="new"></div>'); // on en ajoute (pardi, vu qu'on est là pour ça)
}
}
}

function Resiz(newWi) {
if(newWi > (childWi * le) + childWi) { // si la nouvelle taille du container est sup. à la largeur des .child + 1,
$("div .new").remove(); // on lourde sauvagement toutes les .new.
} else { // autrement
Ajout(); // on les rajoute piteusement
}
}



Voilà l'idée, on peut donc aligner dans une div de taille inconnue des enfants à la seule condition qu'ils soient tous de la même taille (ce qui semble logique en fait).
Je suppose également (je n'ai pas essayé) qu'on doit pouvoir se contenter d'une seule .new dont on modifierait la taille à la demande, voire même d'un pseudo-élément ::after, histoire de grappiller quelques octets...

Si vous remarquez une erreur, ou pour toute question, n'hésitez pas à me contacter !

* https://stackoverflow.com/
** C'est pour le fun ici, mais à chaque fois, on allume Resiz(), ce qui consomme énormément de ressources : il vaudra mieux de loin s'intéresser à stop si c'est possible (à la place de resize)



Modifié le 03-04-2020
Article précédent


Drag and drop avec previsualisation image
Article suivant


Un site multilingue en PHP ? C'est simple !

Commentaires

Pas encore de commentaire

Publier un commentaire :



capcha   



Raccourcis : php css html sql js img

Prévisualiser
GB-Net.fr 2020



fleche haut
Résultats de votre recherche pour :
reduce close