28112 sujets

CSS et mise en forme, CSS3

Modérateur
Bonjour,

Je butte régulièrement sur le problème suivant :

On a une liste d'éléments HTML assez grands tous de même largeur (disons des span avec un css display:inline-block, mais cela pourrait être n'importe quoi d'autre et en particulier des images). On souhaite que ces éléments soient centrés dans leur conteneur (de largeur inconnue, celle-ci variant avec la taille de la fenêtre ouverte par l'internaute). Jusque-là, c'est facile : un text-align:center sur le conteneur suffit.

Mais si l'on se contente de ça, et si la dernière ligne contient moins d'éléments que les autres lignes, on obtient un effet pas terrible la plupart du temps, les éléments de cette dernière ligne étant eux aussi centrés. Et si au lieu d'un text-align:center, on fait un text-align:left, quand les éléments sont assez grands, l'effet n'est pas génial non plus.

On souhaiterait que l'ensemble soit centré, mais que sur la dernière ligne, le premier élément soit aligné avec le premier élément de la ligne du dessus,. Par exemple, pour une liste de 10 éléments, dont 4 tiennent dans la largeur du conteneur, on souhaiterait obtenir (l'ensemble étant centré dans son conteneur) :

XXXX
XXXX
XX

Et si l’internaute réduit sa fenêtre, avec par exemple 3 éléments seulement tenant dans la largeur du conteneur, on souhaiterait obtenir (l'ensemble étant toujours centré dans son conteneur) :

XXX
XXX
XXX
X

J'arrive à faire ça plus ou moins facilement en javascript : voir http://jsfiddle.net/tz01dt1s/, mais au prix d'un fatal manque de fluidité et d'un setTimeout dont on voudrait bien se passer).

Je recherche donc une solution en css pure (tous les coups sont permis, on peut rajouter des éléments HTML si besoin est, et on laisse tomber les vieux navigateurs).

Quelqu'un saurait-il faire ça ?

Note : idéalement, il faudrait ne pas avoir à supposer quoique ce soit sur la largeur du conteneur, et sur la largeur des éléments de la liste (tout ce qu'on sait étant qu'ils ont tous la même largeur). Si l'on connait cette largeur a priori, et si l'on connait le ratio largeur du conteneur par rapport à la largeur de la fenêtre, on pourrait éventuellement s'en sortir à coup de @media (max-width:...) {...}

Ci-dessous le code HTML (les <!-- --> permettant d'éviter la présence de blancs entre les éléments dans le rendu final. D'autres techniques pour supprimer ces blancs existent. j'ai choisis celle-là pour simplifier) :

<div id="A"><!--
--><span>A</span><!--
--><span>B</span><!--
--><span>C</span><!--
--><span>D</span><!--
--><span>E</span><!--
--><span>F</span><!--
--><span>G</span><!--
--><span>H</span><!--
--><span>I</span><!--
--><span>J</span><!--
--></div>

Modifié par parsimonhi (09 Dec 2015 - 18:11)
salut,
du coup on ne parle plus vraiment de "text-align:center" mais plutôt d'un conteneur de tes <span> qui serait centré non ?
Si tu englobais tes <span>, tu pourrais ensuite aisément contrôler avec des MQ.
Au passage pourquoi faire du sport à ton navigateur avec un très couteux "setInterval" quand tu peux passer par un évènement "resize" ?
Modérateur
Zelalsan a écrit :
salut,
du coup on ne parle plus vraiment de "text-align:center" mais plutôt d'un conteneur de tes <span> qui serait centré non ?


En effet, on peut aussi voir les choses comme ça !

Zelalsan a écrit :

Si tu englobais tes <span>, tu pourrais ensuite aisément contrôler avec des MQ.


Justement, je n'ai jamais réussi à obtenir l'effet désiré, même en rajoutant des conteneurs intermédiaires, car le conteneur autour des spans ne vient pas au plus près des spans : il prend toute la largeur du conteneur qui l'entoure et du coup, je reviens au point de départ. Mais il est possible que je passe à côté d'un truc tout bête.

Zelalsan a écrit :

Au passage pourquoi faire du sport à ton navigateur avec un très couteux "setInterval" quand tu peux passer par un évènement "resize" ?


Oui, c'est très couteux ce setTimeout. Si l'on n'est pas trop regardant, déclencher la fonction magic() suite à un resize peut suffire. Mais dans certains cas certes un peu tordus, genre l'internaute s'amuse à faire des zoom texte seul par exemple, ou change la taille de la police, ça ne marche plus me semble-t-il.
Modifié par parsimonhi (11 Dec 2014 - 22:15)
parsimonhi a écrit :
Oui, c'est très couteux ce setTimeout. Si l'on n'est pas trop regardant, déclencher la fonction magic() suite à un resize peut suffire. Mais dans certains cas certes un peu tordus, genre l'internaute s'amuse à faire des zoom texte seul par exemple, ou change la taille de la police, ça ne marche plus me semble-t-il.

Pour autant que je comprenne, quand on change à la souris la taille d'une fenêtre, cela déclenche une flopée d'évènements "resize". Un bon moyen de réguler le fonctionnement est de noter l'heure de déclenchement du premier événement et d'ignorer tous les événements similaires qui sont lancés dans la seconde qui suit.

Mais le mieux évidemment est d'utiliser les possibilités du CSS et de compter sur l'implémentation du navigateur pour remettre les choses en place.

Je dis cela parce que depuis des années, faute de support correct du CSS par les vieux navigateurs d'une part, ma mauvaise connaissance des dernières possibilité d'autre part, j'ai accumulé de nombreuses lignes de code JS sans doute inutiles.

C'est pour me remettre à niveau que je suis devenu récemment un fidèle lecteur de cet indispensable forum.
Modifié par PapyJP (11 Dec 2014 - 23:07)
Modérateur
PapyJP a écrit :

Pour autant que je comprenne, quand on change à la souris la taille d'une fenêtre, cela déclenche une flopée d'évènements "resize".


L'évènement "resize" est difficile à maitriser.
parsimonhi a écrit :
L'évènement "resize" est difficile à maitriser.

Peux tu donner des exemples?
En mettant un "régulateur" comme expliqué plus haut, je n'ai jamais trouvé de cas particulièrement difficile. Regarde ce qui se passe sur mon site http://www.alma-musica.net quand on fait un resize de la page d'accueil. Ce n'est pas génial mais ça modifie la position des éléments et leur taille de façon raisonnablement satisfaisante.
Mais justement à ce sujet, si quelqu'un peut me dire comment obtenir le même resUltat en CSS3 je suis bien volontiers preneur, car on aurait beaucoup plus de souplesse dans le fonctionnement.
C'est le premier point auquel j'aimerais m'atteler pour ce site.
Modifié par PapyJP (12 Dec 2014 - 00:58)
Modérateur
PapyJP a écrit :

Peux tu donner des exemples?


Comme je fais des modules destinés à être intégrés dans des pages faites par des tiers, c'est selon moi assez difficile d'être fiable : en plus du problème que tu as mentionné concernant le déclenchant en rafale de l'évènement, que se passe-t-il en cas d'insertion dans des frames, en cas de zoom, en cas d'affichage sur mobile, en cas d'utilisation du tiers de l'évènement resize lui aussi, ... ? Il est possible que tout ça soit gérable, mais ça nécessite au minimum de se poser des tas de questions.

PapyJP a écrit :

En mettant un "régulateur" comme expliqué plus haut, je n'ai jamais trouvé de cas particulièrement difficile. Regarde ce qui se passe sur mon site http://www.alma-musica.net quand on fait un resize de la page d'accueil. Ce n'est pas génial mais ça modifie la position des éléments et leur taille de façon raisonnablement satisfaisante.
Mais justement à ce sujet, si quelqu'un peut me dire comment obtenir le même resUltat en CSS3 je suis bien volontiers preneur, car on aurait beaucoup plus de souplesse dans le fonctionnement.
C'est le premier point auquel j'aimerais m'atteler pour ce site.


Il me semble, si l'on laisse tomber les vieux navigateurs, qu'avec l'unité vw (1vw=1% de la largeur du viewport) pour la taille des polices et la taille des images, couplé avec l'unité vh (1vh=1% de la hauteur du viewport) ou avec des % pour le positionnement vertical, tu devrais pouvoir t'en sortir sans javascript. Bon exercice ! Je sens que tu vas y passer ton we entier ! Smiley smile

Ceci étant, tout ça ne m'avance pas beaucoup en ce qui concerne la question initiale de ce post !
Modifié par parsimonhi (12 Dec 2014 - 10:56)
Modérateur
Bonjour,

Et tu peux aussi coupler ça à des @media queries (pour par exemple empêcher les polices de devenir trop petites).

Amicalement,
Modérateur
Bonjour,

Je déterre ce vieux sujet parce que j'ai enfin trouvé le moyen de me passer du setInterval() (ou de tout autre artifice qui déclencherait une fonction javascript après le chargement de la page).

La nouvelle fonction magic ne s'exécute qu'en fin de page (juste avant le </body>), et une seule fois. L'idée est d'ajouter quelques éléments au DOM (au moins un de moins que le nombre maximum de colonnes qu'on envisage d'avoir, et au plus la moitié du nombre d'éléments visibles de la liste si on ne connait pas a priori ce nombre de colonne).

Ces éléments sont vides et de hauteur nulle. Mais en utilisant du flex de manière approprié, ces éléments étant bien présents, cela a pour effet de "tasser" à gauche les éléments visibles de la dernière ligne. Le redimensionnement est effectué par le navigateur dès qu'on change la largeur de la fenêtre sans qu'on ait besoin de faire quoi que ce soit. Du coup, outre le fait qu'on n'a plus de setInterval(), on gagne en fluidité.

Voici un exemple de code css (le HTML restant le même que celui que j'ai posté avec ma question) :

#A
{
  display: flex;
  flex-wrap: wrap;
  flex-direction: row; 
  font-size:3em;
  background:green;
  padding:0.125em;
}
#A span
{
  flex:1;
  background:yellow;
  min-width:2em;
  margin:0.125em;
  padding:0;
  text-align:center;
}

Et voici la fonction magic javascript :

function magic(id,tag)
{
    var div,z,e;
    div=document.getElementById(id);
    z=div.getElementsByTagName(tag);
    km=(z.length>>1);
    for (k=0;k<km;k++)
    {
          e=document.createElement(tag);
          e.style.height="0";
          e.style.marginTop="0";
          e.style.marginBottom="0";
          e.style.paddingTop="0";
          e.style.paddingBottom="0";
          e.style.borderTopWidth="0";
          e.style.borderBottomWidth="0";
          div.appendChild(e);
    }
}
magic("A","span");


EDIT (12 janvier 2016) : s'il peut n'y avoir que 2 lignes de boites, il faut utiliser km=z.length-1; au lieu de km=(z.length>>1); S'il n'y a qu'une ligne, elle ne sera pas centrée mais cadrée à gauche.

Et un petit jsfiddle pour ceux qui voudrait tester : http://jsfiddle.net/6e72oa7e/

On peut même envisager de se passer complètement de javascript en ajoutant les éléments vides directement dans le HTML (dans mon exemple, il s'agirait de 5 <span></span>), et ensuite d'ajouter au css le code suivant :

#A span:empty
{
	height:0;
	margin-top:0;
	margin-bottom:0;
	padding-top:0;
	padding-bottom:0;
	border-top-width:0;
	border-bottom-width:0;
}


Amicalement,
Modifié par parsimonhi (12 Jan 2016 - 03:51)