252 votes

Mise en page de la maçonnerie uniquement en CSS

J'ai besoin de mettre en œuvre un aménagement de maçonnerie assez banal. Cependant, pour un certain nombre de raisons, je ne veux pas utiliser JavaScript pour le faire.

A grid of multiple columns of rectangles of varying height.

Paramètres :

  • Tous les éléments ont la même largeur
  • Les éléments ont une hauteur qui ne peut être calculée côté serveur (une image et diverses quantités de texte).
  • Je peux vivre avec un nombre fixe de colonnes si je dois le faire.

il existe une solution triviale qui fonctionne dans les navigateurs modernes, le site column-count propriété.

Le problème de cette solution est que les éléments sont classés en colonnes :

Starting from the top leftmost box, they're numbered 1 through 4 straight down, the topmost box in the next column is 5, and so on.

Alors que j'ai besoin que les éléments soient ordonnés en rangées, au moins approximativement :

Starting from the top leftmost box, they're numbered 1 through 6 straight across, but because box 5 is the shortest the box underneath it is 7 as it has the appearance of being on a row higher than the next box on the far left.

Des approches que j'ai essayées et qui ne fonctionnent pas :

Maintenant, je pourrait modifier le rendu côté serveur et réorganiser les éléments en divisant le nombre d'éléments par le nombre de colonnes, mais c'est compliqué et source d'erreurs (en fonction de la façon dont les navigateurs décident de diviser la liste d'éléments en colonnes), et j'aimerais donc l'éviter si possible.

Y a-t-il une nouvelle magie flexbox qui rend cela possible ?

8 votes

Je ne vois pas de moyen qui ne dépende pas de hauteurs prédéfinies. Si vous reconsidérez JS, jetez un coup d'œil à stackoverflow.com/questions/13518653/ où j'implémente une telle solution qui est assez simple.

4 votes

Je réalise que vous avez dit "uniquement CSS". Je veux juste mentionner que Masonry ne nécessite plus jQuery - la bibliothèque réduite est moins de 8kb - et peut être initialisé avec le html seul. À titre de référence jsfiddle.net/wp7kuk1t

1 votes

Si vous pouvez déterminer la hauteur des éléments à l'avance, en connaissant la hauteur de ligne, la taille de la police (vous devrez utiliser une police spécifique et effectuer des calculs astucieux), la hauteur de l'image, la marge verticale et le remplissage, vous pouvez le faire. Sinon, vous ne pouvez pas le faire en utilisant uniquement CSS. Vous pourriez également utiliser quelque chose comme PhantomJS pour effectuer un pré-rendu de chaque élément et obtenir la hauteur de cet élément, mais cela entraînerait une surcharge/latence significative.

271voto

Michael_B Points 15556

Mise à jour de 2021

CSS Grid Layout Level 3 comprend une masonry fonction.

Le code ressemblera à ceci :

grid-template-rows: masonry
grid-template-columns: masonry

Depuis mars 2021, il n'est disponible que dans Firefox (après avoir activé le drapeau).

fin de la mise à jour ; réponse originale ci-dessous


Flexbox

Une mise en page dynamique de type "masonry" n'est pas possible avec Flexbox, du moins pas de manière propre et efficace.

Flexbox est un système de mise en page unidimensionnel. Cela signifie qu'il peut aligner des éléments le long de lignes horizontales OU verticales. Un élément Flexbox est confiné à sa ligne ou à sa colonne.

Un véritable système de grille est bidimensionnel, ce qui signifie qu'il peut aligner des éléments le long de lignes horizontales ET verticales. Les éléments de contenu peuvent s'étendre simultanément sur les lignes et les colonnes, ce qui est impossible pour les éléments flexibles.

C'est pourquoi flexbox a une capacité limitée pour construire des grilles. C'est aussi une raison pour laquelle le W3C a développé une autre technologie CSS3, Disposition de la grille .


row wrap

Dans un conteneur flex avec flex-flow: row wrap les articles flexibles doivent être emballés dans le nouveau rangées .

Cela signifie que un élément flexible ne peut pas s'enrouler sous un autre élément de la même ligne .

Remarquez ci-dessus comment div #3 enveloppes ci-dessous div #1 en créant une nouvelle ligne. Il ne peut pas s'enrouler sous div #2 .

Par conséquent, lorsque les éléments ne sont pas les plus hauts de la rangée, il reste des espaces blancs, ce qui crée des lacunes inesthétiques.


column wrap

Si vous passez à flex-flow: column wrap une mise en page en forme de grille est plus facile à réaliser. Cependant, un conteneur orienté colonne présente d'emblée quatre problèmes potentiels :

  1. Les éléments Flex s'écoulent verticalement, et non horizontalement (comme dans le cas présent).
  2. Le conteneur se développe horizontalement, et non verticalement (comme la mise en page de Pinterest).
  3. Il faut que le conteneur ait une hauteur fixe, afin que les articles sachent où s'emballer.
  4. À l'heure où nous écrivons ces lignes, il présente un défaut dans tous les principaux navigateurs où le conteneur ne s'étend pas pour accueillir des colonnes supplémentaires .

Par conséquent, un conteneur orienté colonne n'est pas une option dans ce cas, et dans de nombreux autres cas.


Grille CSS avec des dimensions d'éléments non définies

La disposition en grille serait une solution parfaite à votre problème. si les différentes hauteurs des éléments de contenu pouvaient être prédéterminées . Toutes les autres exigences sont tout à fait dans les limites des capacités de Grid.

La largeur et la hauteur des éléments de la grille doivent être connues afin de pouvoir fermer les espaces avec les éléments environnants.

Ainsi, Grid, qui est ce que CSS a de mieux à offrir pour construire une mise en page de type maçonnerie à flux horizontal, n'est pas à la hauteur dans ce cas.

En fait, jusqu'à l'arrivée d'une technologie CSS capable de combler automatiquement les lacunes, les CSS en général n'ont pas de solution. Une telle solution nécessiterait probablement de refondre le document, et je ne suis donc pas sûr de son utilité ou de son efficacité.

Vous aurez besoin d'un script.

Les solutions JavaScript ont tendance à utiliser le positionnement absolu, qui supprime les éléments de contenu du flux de documents afin de les réorganiser sans discontinuité. Voici deux exemples :

Masonry est une bibliothèque de mise en page de grille JavaScript. Elle fonctionne en plaçant les éléments dans une position optimale en fonction de l'espace vertical disponible. l'espace vertical disponible, un peu comme un maçon qui place des pierres dans un mur.

source : http://masonry.desandro.com/

Pinterest] est un site vraiment cool, mais ce que je trouve intéressant, c'est la façon dont ces pinboards sont disposés... Le but de ce tutoriel est donc de recréer nous-mêmes cet effet de bloc réactif...

source : https://benholland.me/javascript/2012/02/20/how-to-build-a-site-that-works-like-pinterest.html


Grille CSS avec des dimensions d'éléments définies

Pour les mises en page dont la largeur et la hauteur des éléments de contenu sont connues, voici une mise en page en maçonnerie à flux horizontal en pur CSS :

grid-container {
  display: grid;                                                /* 1 */
  grid-auto-rows: 50px;                                         /* 2 */
  grid-gap: 10px;                                               /* 3 */
  grid-template-columns: repeat(auto-fill, minmax(30%, 1fr));   /* 4 */
}

[short] {
  grid-row: span 1;                                             /* 5 */
  background-color: green;
}

[tall] {
  grid-row: span 2;
  background-color: crimson;
}

[taller] {
  grid-row: span 3;
  background-color: blue;
}

[tallest] {
  grid-row: span 4;
  background-color: gray;
}

grid-item {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.3em;
  font-weight: bold;
  color: white;
}

<grid-container>
  <grid-item short>01</grid-item>
  <grid-item short>02</grid-item>
  <grid-item tall>03</grid-item>
  <grid-item tall>04</grid-item>
  <grid-item short>05</grid-item>
  <grid-item taller>06</grid-item>
  <grid-item short>07</grid-item>
  <grid-item tallest>08</grid-item>
  <grid-item tall>09</grid-item>
  <grid-item short>10</grid-item>
  <grid-item tallest>etc.</grid-item>
  <grid-item tall></grid-item>
  <grid-item taller></grid-item>
  <grid-item short></grid-item>
  <grid-item short></grid-item>
  <grid-item short></grid-item>
  <grid-item short></grid-item>
  <grid-item tall></grid-item>
  <grid-item short></grid-item>
  <grid-item taller></grid-item>
  <grid-item short></grid-item>
  <grid-item tall></grid-item>
  <grid-item short></grid-item>
  <grid-item tall></grid-item>
  <grid-item short></grid-item>
  <grid-item short></grid-item>
  <grid-item tallest></grid-item>
  <grid-item taller></grid-item>
  <grid-item short></grid-item>
  <grid-item tallest></grid-item>
  <grid-item tall></grid-item>
  <grid-item short></grid-item>
</grid-container>

Démonstration de jsFiddle


Comment cela fonctionne

  1. Établir un conteneur de grille au niveau du bloc. ( inline-grid serait l'autre option)
  2. Le site grid-auto-rows définit la hauteur des lignes générées automatiquement. Dans cette grille, chaque ligne a une hauteur de 50px.
  3. Le site grid-gap est un raccourci pour grid-column-gap et grid-row-gap . Cette règle fixe un écart de 10px entre éléments de la grille. (Elle ne s'applique pas à la zone située entre les éléments et le conteneur).
  4. Le site grid-template-columns définit la largeur des colonnes explicitement définies.

Le site repeat définit un motif de colonnes (ou de lignes) répétitives.

Le site auto-fill indique à la grille d'aligner autant de colonnes (ou de lignes) que possible sans faire déborder le conteneur. (Cela peut créer un comportement similaire à celui de la fonction Flex Layout flex-wrap: wrap .)

Le site minmax() définit une plage de taille minimale et maximale pour chaque colonne (ou ligne). Dans le code ci-dessus, la largeur de chaque colonne sera au minimum de 30% du conteneur et au maximum de l'espace libre disponible.

Le site fr unité représente une fraction de l'espace libre dans le conteneur de la grille. Elle est comparable à la fonction flex-grow propriété.

  1. Avec grid-row et span nous indiquons aux éléments de la grille sur combien de lignes ils doivent s'étendre.

Prise en charge des grilles CSS par les navigateurs

  • Chrome - prise en charge complète à partir du 8 mars 2017 (version 57).
  • Firefox - support complet à partir du 6 mars 2017 (version 52)
  • Safari - prise en charge complète à partir du 26 mars 2017 (version 10.1).
  • Edge - prise en charge complète à partir du 16 octobre 2017 (version 16).
  • IE11 - pas de prise en charge de la spécification actuelle ; prise en charge d'une version obsolète

Voici le tableau complet : http://caniuse.com/#search=grid


Fonction de superposition de la grille dans Firefox

Dans les outils de développement de Firefox, lorsque vous inspectez le conteneur de la grille, une petite icône de grille apparaît dans la déclaration CSS. En cliquant dessus, elle affiche un contour de votre grille sur la page.

Plus de détails ici : https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Examine_grid_layouts

8 votes

Une réponse fantastique ! Avec la solution "Grille CSS avec dimensions des éléments définies", est-il possible d'exprimer la hauteur d'une cellule en pourcentage de la largeur de la cellule ? Cela serait utile pour l'affichage d'images, dont le rapport d'aspect est connu. Nous voulons maintenir le rapport d'aspect à tout moment.

0 votes

Merci. En ce qui concerne la question du ratio d'aspect pour les images, la méthode du "pourcentage de la largeur de la cellule" (l'astuce du pourcentage de remplissage ?), elle n'est pas fiable dans Grid, ni dans Flexbox, d'ailleurs. Voir ici et ici . @OliverJosephAsh

0 votes

Oui, je fais référence à l'astuce du remplissage en pourcentage. Est-il possible d'utiliser l'une de ces solutions de contournement en combinaison avec votre solution de mise en page en maçonnerie ? Dans ce contexte, nous cherchons à mettre en œuvre une disposition de maçonnerie uniquement en CSS pour les images de la page d'accueil. unsplash.com .

27voto

Oliver Joseph Ash Points 571

Il s'agit d'une technique récemment découverte impliquant la flexbox : https://tobiasahlin.com/blog/masonry-with-css/ .

L'article me semble logique, mais je n'ai pas essayé de l'utiliser, donc je ne sais pas s'il y a des réserves, autres que celles mentionnées dans la réponse de Michael.

Voici un extrait de l'article, qui utilise la fonction order la propriété, combinée à :nth-child .

Extrait de pile

.container {
  display: flex;
  flex-flow: column wrap;
  align-content: space-between;
  /* Your container needs a fixed height, and it 
   * needs to be taller than your tallest column. */
  height: 960px;

  /* Optional */
  background-color: #f7f7f7;
  border-radius: 3px;
  padding: 20px;
  width: 60%;
  margin: 40px auto;
  counter-reset: items;
}

.item {
  width: 24%;
  /* Optional */
  position: relative;
  margin-bottom: 2%;
  border-radius: 3px;
  background-color: #a1cbfa;
  border: 1px solid #4290e2;
  box-shadow: 0 2px 2px rgba(0,90,250,0.05),
    0 4px 4px rgba(0,90,250,0.05),
    0 8px 8px rgba(0,90,250,0.05),
    0 16px 16px rgba(0,90,250,0.05);
  color: #fff;
  padding: 15px;
  box-sizing: border-box;
}

 /* Just to print out numbers */
div.item::before {
  counter-increment: items;
  content: counter(items);
}

/* Re-order items into 3 rows */
.item:nth-of-type(4n+1) { order: 1; }
.item:nth-of-type(4n+2) { order: 2; }
.item:nth-of-type(4n+3) { order: 3; }
.item:nth-of-type(4n)   { order: 4; }

/* Force new columns */
.break {
  flex-basis: 100%;
  width: 0;
  border: 1px solid #ddd;
  margin: 0;
  content: "";
  padding: 0;
}

body { font-family: sans-serif; }
h3 { text-align: center; }

<div class="container">
  <div class="item" style="height: 140px"></div>
  <div class="item" style="height: 190px"></div>
  <div class="item" style="height: 170px"></div>
  <div class="item" style="height: 120px"></div>
  <div class="item" style="height: 160px"></div>
  <div class="item" style="height: 180px"></div>
  <div class="item" style="height: 140px"></div>
  <div class="item" style="height: 150px"></div>
  <div class="item" style="height: 170px"></div>
  <div class="item" style="height: 170px"></div>
  <div class="item" style="height: 140px"></div>
  <div class="item" style="height: 190px"></div>
  <div class="item" style="height: 170px"></div>
  <div class="item" style="height: 120px"></div>
  <div class="item" style="height: 160px"></div>
  <div class="item" style="height: 180px"></div>
  <div class="item" style="height: 140px"></div>
  <div class="item" style="height: 150px"></div>
  <div class="item" style="height: 170px"></div>
  <div class="item" style="height: 170px"></div>

  <span class="item break"></span>
  <span class="item break"></span>
  <span class="item break"></span>
</div>

1 votes

Tout d'abord, les réponses qui ne contiennent que des liens sont mauvaises, car si un tel lien disparaît, la réponse aussi. Deuxièmement, si vous lisez plus attentivement la réponse donnée, vous constaterez que ce qui est mentionné dans votre lien est couvert. Pour faire simple, il y a trop de limitations à l'utilisation de Flexbox seul, à moins que ce qui est mentionné ci-dessus ne soit pas un problème.

4 votes

J'ai lu très attentivement la réponse acceptée - vous verrez même que j'ai ajouté des commentaires au bas de la réponse. L'approche flexbox dont j'ai parlé est unique et n'est pas couverte par cette réponse. Je ne veux pas répéter l'article, car c'est très compliqué, et l'article le couvre très bien.

1 votes

La seule chose que le lien fait différemment, c'est l'utilisation de l'outil de gestion de l'information. order ce qui donne l'impression que les éléments circulent de gauche à droite. Cependant, son principal atout est flex-flow: column wrap qui est mentionné dans la réponse ci-dessus. De plus, il ne peut pas faire circuler les éléments de la même manière qu'une vraie maçonnerie, par exemple, si les 3ème et 4ème éléments pouvaient être placés dans la 3ème colonne, ils le seraient, mais pas la colonne liée. Mais comme je l'ai dit, tout dépend des exigences, et dans ce cas (cette question), cela ne fonctionnera pas comme demandé.

3voto

Temani Afif Points 69370

Enfin une solution uniquement en CSS pour créer facilement une disposition en maçonnerie, mais il faut être patient car il n'y a pas de support pour l'instant.

Cette fonctionnalité a été introduite dans le Module de mise en page de la grille CSS niveau 3

Ce module présente la disposition en maçonnerie comme un mode de disposition supplémentaire pour les conteneurs CSS Grid.

Puis

La disposition en maçonnerie est prise en charge pour les conteneurs de grille en spécifiant la valeur maçonnerie pour l'un de ses axes. Cet axe est appelé l'axe de la maçonnerie, et l'autre axe est appelé l'axe de la grille.

Un exemple de base serait :

.container {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  grid-template-rows: masonry; /* this will do the magic */
  grid-gap: 10px;
}

img {
  width: 100%;
}

<div class="container">
  <img src="https://picsum.photos/id/1/200/300">
  <img src="https://picsum.photos/id/17/200/400">
  <img src="https://picsum.photos/id/18/200/100">
  <img src="https://picsum.photos/id/107/200/200">
  <img src="https://picsum.photos/id/1069/200/600">
  <img src="https://picsum.photos/id/12/200/200">
  <img src="https://picsum.photos/id/130/200/100">
  <img src="https://picsum.photos/id/203/200/100">
  <img src="https://picsum.photos/id/109/200/200">
  <img src="https://picsum.photos/id/11/200/100">
</div>

Cela donnera le résultat suivant sur Firefox si vous activez la fonction comme expliqué ici : https://caniuse.com/?search=masonry

  1. Ouvrez Firefox et écrivez about:config dans la barre d'adresse.
  2. faites une recherche en utilisant maçonnerie
  3. vous n'aurez qu'un seul drapeau, faites en sorte qu'il soit vrai

CSS masonry layout

Si on réduit l'écran, la partie réponsive est parfaite !

Masonry layout using CSS only

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X