Flexbox: 4 articles par ligne

260

J'utilise une boîte flexible pour afficher 8 éléments qui seront redimensionnés dynamiquement avec ma page. Comment le forcer à diviser les éléments en deux rangées? (4 par rangée)?

Voici un extrait pertinent:

(Ou si vous préférez jsfiddle - http://jsfiddle.net/vivmaha/oq6prk1p/2/ )

.parent-wrapper {
  height: 100%;
  width: 100%;
  border: 1px solid black;
}
.parent {
  display: flex;
  font-size: 0;
  flex-wrap: wrap;
  margin: -10px 0 0 -10px;
}
.child {
  display: inline-block;
  background: blue;
  margin: 10px 0 0 10px;
  flex-grow: 1;
  height: 100px;
}
<body>
  <div class="parent-wrapper">
    <div class="parent">
      <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 class="child"></div>
      <div class="child"></div>
    </div>
  </div>
</body>

Vivek Maharajh
la source
16
[Mise à jour] Cette question était basée sur une mauvaise conception qui ne répond pas. Si vous vous retrouvez à utiliser l'une des réponses ci-dessous, soyez prudent. Dans une bonne conception, vous auriez plus d'éléments sur des écrans plus larges et moins sur des écrans plus petits. Forcer 4 éléments pour toutes les tailles d'écran n'aura l'air attrayant que sur une gamme étroite de largeurs d'écran.
Vivek Maharajh

Réponses:

382

Vous avez flex-wrap: wrapsur le conteneur. C'est bien, car il remplace la valeur par défaut, qui est nowrap( source ). C'est la raison pour laquelle les éléments ne s'enroulent pas pour former une grille dans certains cas .

Dans ce cas, le problème principal flex-grow: 1concerne les éléments flexibles.

La flex-growpropriété ne dimensionne pas réellement les éléments flexibles. Sa tâche est de distribuer de l'espace libre dans le conteneur ( source ). Donc, peu importe la taille de l'écran, chaque élément recevra une partie proportionnelle de l'espace libre sur la ligne.

Plus précisément, votre conteneur contient huit articles flexibles. Avec flex-grow: 1, chacun reçoit 1/8 de l'espace libre sur la ligne. Puisqu'il n'y a pas de contenu dans vos articles, ils peuvent se réduire à une largeur nulle et ne seront jamais encapsulés.

La solution est de définir une largeur sur les articles. Essaye ça:

.parent {
  display: flex;
  flex-wrap: wrap;
}

.child {
  flex: 1 0 21%; /* explanation below */
  margin: 5px;
  height: 100px;
  background-color: blue;
}
<div class="parent">
  <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 class="child"></div>
  <div class="child"></div>
</div>

Avec flex-grow: 1défini dans la flexsténographie, il n'est pas nécessaire flex-basisd'être à 25%, ce qui entraînerait en fait trois éléments par ligne en raison des marges.

Étant donné flex-growque consommera de l'espace libre sur la ligne, flex-basisil doit seulement être suffisamment grand pour appliquer un habillage. Dans ce cas, avec flex-basis: 21%, il y a beaucoup d'espace pour les marges, mais jamais assez d'espace pour un cinquième élément.

Michael Benjamin
la source
1
J'aime ça parce que cela a fonctionné pour moi, mais «beaucoup d'espace» me fait un peu mal à l'aise. Y a-t-il une toute petite fenêtre ou situation où cette «estimation» pourrait me faire défaut?
Jackson
1
Quelle sorte de situation? Dans la plupart des mises en page, il existe des cas extrêmes qui peuvent créer des problèmes. De tels cas passent normalement par des analyses coûts-avantages parce qu'ils ne se produisent jamais, ou presque jamais. Par exemple, dans la solution ci-dessus, si vous mettez de très grandes marges, la mise en page peut se casser. Si c'est un problème, alors il devrait être affiché dans la question. @Jackson
Michael Benjamin
2
"Si vous mettez de très grandes marges, la mise en page peut se casser" C'est un exemple de scénario que j'aimerais éviter. Je me fiche de l'aspect pratique; divertir mon adoration pour la perfection. Existe-t-il un moyen d'être plus précis?
Jackson
27
Vous pouvez également utiliserflex: 1 0 calc(25% - 10px);
MarkG
2
Très bonne réponse! Une petite amélioration que j'utilise est margin: 2%;au lieu d'un nombre de pixels fixe afin d'empêcher les boîtes de sauter vers une nouvelle ligne sur les petits appareils / résolutions. la formule générale est (100 - (4 * box-width-percent)) / 8% pour différentes largeurs de boîte
thomaspsk
104

Ajoutez une largeur aux .childéléments. Personnellement, j'utiliserais des pourcentages sur le margin-leftsi vous voulez qu'il soit toujours 4 par ligne.

DEMO

.child {
    display: inline-block;
    background: blue;
    margin: 10px 0 0 2%;
    flex-grow: 1;
    height: 100px;
    width: calc(100% * (1/4) - 10px - 1px);
}
dowomenfart
la source
Bonne idée. J'ai utilisé cette idée avec des modifications mineures. jsfiddle.net/vivmaha/oq6prk1p/6
Vivek Maharajh
1
Ah. En fait, cela ne résout pas mon problème. Voir ici pour un exemple: jsfiddle.net/vivmaha/oq6prk1p/7 . Je n'ai pas la liberté d'utiliser des pourcentages pour ma marge. Ils doivent avoir une largeur fixe. Cela signifie que la magie de 23% que nous avons choisie ne fonctionnera pas car elle ne tient pas compte de la marge fixe.
Vivek Maharajh
3
Oui, calc le résout! «calc (100% * (1/4) - 10px - 1px)». Voir jsfiddle.net/vivmaha/oq6prk1p/9
Vivek Maharajh
24
Alors à quoi sert le flex dans tout ça?
twicejr
Qu'en est-il du design réactif? Nous ne voulons pas mettre une largeur fixe sur l'enfant
bougeoir
40

Voici une autre approche.

Vous pouvez également le faire de cette manière:

.parent{
  display: flex;
  flex-wrap: wrap;
}

.child{
  width: 25%;
  box-sizing: border-box;
}

Exemple: https://codepen.io/capynet/pen/WOPBBm

Et un échantillon plus complet: https://codepen.io/capynet/pen/JyYaba

Capy
la source
1
Et si j'ai moins de 4 enfants? Je n'aime pas cet espace vide vide, les enfants ne sont pas
réactifs
L'autre réponse de Muddasir Abbas n'utilise que des paramètres flex, donc je pense que c'est une solution plus propre.
Andrew Koster
17

Je le ferais comme ça en utilisant des marges négatives et du calc pour les gouttières:

.parent {
  display: flex;
  flex-wrap: wrap;
  margin-top: -10px;
  margin-left: -10px;
}

.child {
  width: calc(25% - 10px);
  margin-left: 10px;
  margin-top: 10px;
}

Démo: https://jsfiddle.net/9j2rvom4/


Autre méthode de grille CSS:

.parent {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-column-gap: 10px;
  grid-row-gap: 10px;
}

Démo: https://jsfiddle.net/jc2utfs3/

Shanomurphy
la source
14
<style type="text/css">
.parent{
    display: flex;
    flex-wrap: wrap;
}
.parent .child{
    flex: 1 1 25%;
}
</style>    
<div class="parent">
      <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 class="child"></div>
      <div class="child"></div>
    </div>

J'espère que ça aide. pour plus de détails vous pouvez suivre ce lien

Muddasir Abbas
la source
4

Je crois que cet exemple est plus simple et plus facile à comprendre que @dowomenfart.

.child {
    display: inline-block;
    margin: 0 1em;
    flex-grow: 1;
    width: calc(25% - 2em);
}

Cela permet d'effectuer les mêmes calculs de largeur tout en coupant directement à la viande. Le calcul est beaucoup plus facile et emconstitue la nouvelle norme en raison de son évolutivité et de sa convivialité mobile.

Joseph Cho
la source
3
em n'est pas vraiment la nouvelle norme. Cela peut être pénible et les concepteurs ne conçoivent pas en pensant aux EM.
Danny van Holten
2

.parent-wrapper {
	height: 100%;
	width: 100%;
	border: 1px solid black;
}
	.parent {
	display: flex;
	font-size: 0;
	flex-wrap: wrap;
	margin-right: -10px;
	margin-bottom: -10px;
}
	.child {
	background: blue;
	height: 100px;
	flex-grow: 1;
	flex-shrink: 0;
	flex-basis: calc(25% - 10px);
}
	.child:nth-child(even) {
	margin: 0 10px 10px 10px;
	background-color: lime;
}
	.child:nth-child(odd) {
	background-color: orange; 
}
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<style type="text/css">

	</style>
</head>
<body>
  <div class="parent-wrapper">
    <div class="parent">
      <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 class="child"></div>
      <div class="child"></div>
    </div>
  </div>
</body>
</html>

;)

Mateus Manosso Barszcz
la source
1
Si vous pouvez ajouter un peu de contexte sur la raison pour laquelle cela répond à la question OP plutôt que de simplement publier du code.
d219
si vous ajoutez justify-content: space-evenly;au prent, ils s'alignent bien et vous n'avez pas à faire calcl'enfant. Merci pour l'exemple!
Mattijs
0

Flex wrap + marge négative

Pourquoi flex vs display: inline-block?

Pourquoi une marge négative?

Soit vous utilisez SCSS ou CSS-in-JS pour les cas de bord (c'est-à-dire le premier élément de la colonne), soit vous définissez une marge par défaut et vous vous débarrassez de la marge extérieure plus tard.

la mise en oeuvre

https://codepen.io/zurfyx/pen/BaBWpja

<div class="outerContainer">
    <div class="container">
        <div class="elementContainer">
            <div class="element">
            </div>
        </div>
        ...
    </div>
</div>
:root {
  --columns: 2;
  --betweenColumns: 20px; /* This value is doubled when no margin collapsing */
}

.outerContainer {
    overflow: hidden; /* Hide the negative margin */
}

.container {
    background-color: grey;
    display: flex;
    flex-wrap: wrap;
    margin: calc(-1 * var(--betweenColumns));
}

.elementContainer {
    display: flex; /* To prevent margin collapsing */
    width: calc(1/var(--columns) * 100% - 2 * var(--betweenColumns));
    margin: var(--betweenColumns);
}

.element {
    display: flex;
    border: 1px solid red;
    background-color: yellow;
    width: 100%;
    height: 42px;
}
zurfyx
la source
-12

Voici une autre façon sans utiliser calc().

// 4 PER ROW
// 100 divided by 4 is 25. Let's use 21% for width, and the remainder 4% for left & right margins...
.child {
  margin: 0 2% 0 2%;
  width: 21%;
}

// 3 PER ROW
// 100 divided by 3 is 33.3333... Let's use 30% for width, and remaining 3.3333% for sides (hint: 3.3333 / 2 = 1.66666)
.child {
  margin: 0 1.66666% 0 1.66666%;
  width: 30%;
}

// and so on!

C'est tout ce qu'on peut en dire. Vous pouvez devenir fantaisiste avec les dimensions pour obtenir des tailles plus esthétiques, mais c'est l'idée.

wle8300
la source
6
Je n'ai pas testé cela, mais la syntaxe CSS est incorrecte (les valeurs CSS ne sont pas citées et les déclarations doivent se terminer par un point-virgule), donc cela ne fonctionnera certainement pas dans sa forme actuelle.
Nick F