Comment spécifier des sauts de ligne dans une mise en page multi-lignes flexbox?

236

Existe-t-il un moyen de faire une coupure de ligne dans une flexbox à plusieurs lignes?

Par exemple, pour casser après chaque 3ème élément dans ce CodePen .

.container {
  background: tomato;
  display: flex;
  flex-flow: row wrap;
  align-content: space-between;
  justify-content: space-between;
}
.item {
  width: 100px;
  height: 100px;
  background: gold;
  border: 1px solid black;
  font-size: 30px;
  line-height: 100px;
  text-align: center;
  margin: 10px;
}
.item:nth-child(3n) {
  background: silver;
}
<div class="container">
  <div class="item">1</div>
  <div class="item">2</div>
  <div class="item">3</div>
  <div class="item">4</div>
  <div class="item">5</div>
  <div class="item">6</div>
  <div class="item">7</div>
  <div class="item">8</div>
  <div class="item">9</div>
  <div class="item">10</div>
</div>

Comme

.item:nth-child(3n){
  /* line-break: after; */    
}
Artem Svirskyi
la source
1
J'ai eu le même problème ou un problème très similaire; Je voulais casser chaque 4ème élément, j'ai donc simplement défini la largeur de chaque élément flexible à 25vw (ou 25%). Donc, dans votre cas, pour chaque 3ème élément, vous utiliserez 33,3vw (ou 33,3%). A parfaitement fonctionné pour ce que je voulais. Pourrait aider quelqu'un d'autre s'il recherche une méthode plus simple.
Ben Clarke
Ben Clarke! Merci beaucoup! Votre réponse est la seule qui a fonctionné. Vous pourriez envisager de l'ajouter comme réponse. :-)
itmuckel

Réponses:

323

La solution la plus simple et la plus fiable consiste à insérer des éléments flexibles aux bons endroits. S'ils sont suffisamment larges ( width: 100%), ils forceront un saut de ligne.

Mais c'est moche et pas sémantique. Au lieu de cela, nous pourrions générer des pseudo-éléments à l'intérieur du conteneur flexible et les utiliser orderpour les déplacer aux bons endroits.

Mais il y a une limitation: le conteneur flexible ne peut avoir qu'un ::beforeet un ::afterpseudo-élément. Cela signifie que vous ne pouvez forcer que 2 sauts de ligne.

Pour résoudre ce problème, vous pouvez générer les pseudo-éléments à l'intérieur des éléments flexibles plutôt que dans le conteneur flex. De cette façon, vous ne serez pas limité à 2. Mais ces pseudo-éléments ne seront pas des éléments flexibles, ils ne pourront donc pas forcer les sauts de ligne.

Mais heureusement, CSS Display L3 a introduit display: contents(actuellement uniquement pris en charge par Firefox 37):

L'élément lui-même ne génère aucune boîte, mais ses enfants et pseudo-éléments génèrent toujours des boîtes comme d'habitude. Aux fins de la génération et de la mise en page des boîtes, l'élément doit être traité comme s'il avait été remplacé par ses enfants et pseudo-éléments dans l'arborescence du document.

Vous pouvez donc appliquer display: contentsaux enfants du conteneur flexible et envelopper le contenu de chacun dans un emballage supplémentaire. Ensuite, les éléments flexibles seront ces enveloppes supplémentaires et les pseudo-éléments des enfants.

Alternativement, selon Fragmenting Flex Layout et CSS Fragmentation , Flexbox autorise les interruptions forcées en utilisant break-before, break-afterou leurs alias CSS 2.1:

.item:nth-child(3n) {
  page-break-after: always; /* CSS 2.1 syntax */
  break-after: always; /* New syntax */
}

Les sauts de ligne forcés dans Flexbox ne sont pas encore largement pris en charge, mais cela fonctionne sur Firefox.

Oriol
la source
@Oriol À propos de la première approche, pourquoi dites-vous qu'elle est moche et non sémantique? Juste curieux.
nacho4d
18
@ nacho4d Parce que HTML ne doit pas être modifié à des fins de style. Et si vous changez d'avis et décidez que vous voulez 4 colonnes au lieu de 3, vous devrez peut-être modifier beaucoup de code HTML. Comparez avec la break-aftersolution, qui ne nécessiterait que la modification d'un sélecteur dans la feuille de style.
Oriol
1
J'ai dû ajouter display: block;aux classes .container ::beforeet ::afterpseudo pour faire fonctionner la solution numéro deux dans IE. YMMV!
retors
1
@twined C'est étrange, car les éléments flexibles devraient être bloqués automatiquement.
Oriol
2
Étant donné que le saut de page a apparemment été supprimé de la spécification, est-il possible d'obtenir votre deuxième extrait de code dans la direction de la colonne et de ne pas le faire augmenter la hauteur de son conteneur? Je n'ai pas eu de chance et le fait de fixer la base flexible à 100% / articles étend sa hauteur.
Lucent
42

De mon point de vue, il est plus sémantique d'utiliser des <hr> éléments comme sauts de ligne entre les éléments flexibles.

.container {
  display: flex;
  flex-flow: wrap;
}

.container hr {
  width: 100%;
}
<div class="container">
  <div>1</div>
  <div>2</div>
  <hr>
  <div>3</div>
  <div>2</div>
  ...
</div>

Testé dans Chrome 66, Firefox 60 et Safari 11.

Petr Stepanov
la source
7
C'est comme ça que je le fais aussi, ça marche très bien. Ajouter hr {flex-base: 100%; hauteur: 0; marge: 0; bordure: 0; } rend la rupture transparente.
Besworks
J'aime cette approche. Remarque: lorsque vous utilisez gap: 10px;la distance entre les lignes est en fait 20px. Pour adresse, spécifier un intervalle de rangée de la moitié de cette taille: gap: 5px 10px;.
CuddleBunny
@Besworks: borderdevrait être défini sur none, au lieu de0
Mark
@mark, border:0;est tout aussi valide que border:none;. Voir: stackoverflow.com/questions/2922909/…
Besworks
23

@Oriol a une excellente réponse, malheureusement en octobre 2017, ni l'un ni l' display:contentsautre page-break-aftern'est largement pris en charge, il vaut mieux dire qu'il s'agit de Firefox qui le supporte mais pas des autres joueurs, j'ai trouvé le "hack" suivant que je considère mieux que difficile coder dans une pause après chaque 3ème élément, car cela rendra très difficile la convivialité de la page mobile.

Comme dit, c'est un hack et l'inconvénient est que vous devez ajouter pas mal d'éléments supplémentaires pour rien, mais il fait l'affaire et fonctionne sur plusieurs navigateurs même sur le IE11 daté.

Le "hack" consiste simplement à ajouter un élément supplémentaire après chaque div, qui est défini sur display:nonepuis utilisé le CSS nth-childpour décider lequel de ceux-ci doit être rendu visible en forçant un frein de ligne comme ceci:

.container {
  background: tomato;
  display: flex;
  flex-flow: row wrap;
  justify-content: space-between;
}
.item {
  width: 100px;
  background: gold;
  height: 100px;
  border: 1px solid black;
  font-size: 30px;
  line-height: 100px;
  text-align: center;
  margin: 10px
}
.item:nth-child(3n-1) {
  background: silver;
}
.breaker {display:none;}
.breaker:nth-child(3n) {
  display:block;
  width:100%;
  height:0;
 }
<div class="container">
  <div class="item">1</div><p class=breaker></p>
  <div class="item">2</div><p class=breaker></p>
  <div class="item">3</div><p class=breaker></p>
  <div class="item">4</div><p class=breaker></p>
  <div class="item">5</div><p class=breaker></p>
  <div class="item">6</div><p class=breaker></p>
  <div class="item">7</div><p class=breaker></p>
  <div class="item">8</div><p class=breaker></p>
  <div class="item">9</div><p class=breaker></p>
  <div class="item">10</div><p class=breaker></p>
</div>

Emil Borconi
la source
2
J'ai également constaté que les méthodes "display: contents" et "page-break-after" ne fonctionnent pas, et j'ai eu recours à ce "hack". Cela a été signalé comme un bug Chrome et marqué comme "WontFix" (voir bugs.chromium.org/p/chromium/issues/detail?id=473481 ) avec l'explication: "Il n'y a, selon le CSS Working Group, aucun manière actuelle de forcer un saut de ligne dans une boîte flexible avec CSS. "
Martin_W
Vous pouvez économiser une touche d'encombrement en utilisant le sélecteur .container>p. Ensuite, toutes ces <p></p>balises n'auraient pas besoin de l' classattribut. Pas important bien sûr. Juste mon cerveau paresseux qui trouve un petit ajustement peu encombrant à votre solution intelligente. Bien sûr, il repose également sur le fait que l'utilisateur n'a pas d'autres <p>balises en tant qu'enfants directs de la .containerdiv. Techniquement, vous pourriez faire de même avec tous les autres <div>enfants, mais vous avez beaucoup plus de chances d'avoir d'autres <div>s dans le s .containerque vous <p>, donc ce n'est probablement pas un choix judicieux.
Steve
13

Vous voulez un saut de ligne sémantique?

Pensez ensuite à utiliser <br>. W3Schools peut vous suggérer que BRc'est juste pour écrire des poèmes (le mien arrive bientôt) mais vous pouvez changer le style pour qu'il se comporte comme un élément de bloc de 100% de largeur qui poussera votre contenu à la ligne suivante. Si «br» suggère une pause, cela me semble plus approprié que d'utiliser hrou 100% divet rend le HTML plus lisible.

Insérez l' <br>endroit où vous avez besoin de sauts de ligne et stylisez-le comme ceci.

 // Use `>` to avoid styling `<br>` inside your boxes 
 .container > br 
 {
    width: 100%;
    content: '';
 }

Vous pouvez désactiver <br>avec les requêtes multimédias , en définissant display:sur blockou noneselon le cas (j'ai inclus un exemple de cela mais je l'ai laissé en commentaire).

Vous pouvez également utiliser order:pour définir la commande si nécessaire.

Et vous pouvez en mettre autant que vous le souhaitez, avec différentes classes ou noms :-)

.container {
  background: tomato;
  display: flex;
  flex-flow: row wrap;
  justify-content: space-between;
}
.item {
  width: 100px;
  background: gold;
  height: 100px;
  border: 1px solid black;
  font-size: 30px;
  line-height: 100px;
  text-align: center;
  margin: 10px
}

.container > br
{
  width: 100%;
  content: '';
}

// .linebreak1 
// { 
//    display: none;
// }

// @media (min-width: 768px) 
// {
//    .linebreak1
//    {
//       display: block;
//    }
// }
<div class="container">
  <div class="item">1</div>
  <div class="item">2</div>
  <br class="linebreak1"/>
  <div class="item">3</div>
  <div class="item">4</div>
  <div class="item">5</div>
  <div class="item">6</div>
  <div class="item">7</div>
  <div class="item">8</div>
  <div class="item">9</div>
  <div class="item">10</div>
</div>


Pas besoin de vous limiter à ce que dit W3Schools:

entrez la description de l'image ici

Simon_Weaver
la source
Une extension de la technique consiste à mettre <br class="2col">après chaque deuxième élément, <br class="3col">après chaque troisième. Ensuite, appliquez une classe cols-2au conteneur et créez css pour activer uniquement les sauts de ligne appropriés pour ce nombre de colonnes. par exemple. br { display: none; } .cols-2 br.2col { display: block; }
Simon_Weaver
Non, a brn'est pas pour les éléments de saut de ligne , c'est pour le texte : developer.mozilla.org/en-US/docs/Web/HTML/Element/br ... stackoverflow.com/questions/3937515/…
Ason
2
Je vais changer ma formulation afin de ne pas présenter cela comme une solution parfaite, mais dans certains cas, je ne vois pas cela comme pire que d'autres solutions div ou pseudo-élément. Je vais peut-être aller écrire un poème à ce sujet maintenant.
Simon_Weaver
Ouais ... un poème serait bien, n'oubliez pas de poster un lien ici :) ... Concernant une solution parfaite, il y en a un ( break-*montré dans la réponse acceptée) bien que malheureusement il n'ait pas encore atteint les navigateurs croisés , donc le deuxième meilleur est d'utiliser un élément qui remplit nativement la largeur de son parent et de pousser tous les frères et sœurs suivants sur une ligne d'eux-mêmes, ce qui est à nouveau donné dans la réponse acceptée. Donc, en utilisant tout autre élément qu'un bloc comme celui-ci serait pire, sémantiquement, comme le br.
Ason
5
N'oubliez pas que vous publiez une impression de W3Schools, pas de W3C, ils ne sont pas connectés.
Edu Ruiz
7

Je pense que la méthode traditionnelle est flexible et assez facile à comprendre:

Marquage

<div class="flex-grid">
    <div class="col-4">.col-4</div>
    <div class="col-4">.col-4</div>
    <div class="col-4">.col-4</div>

    <div class="col-4">.col-4</div>
    <div class="col-4">.col-4</div>
    <div class="col-4">.col-4</div>

    <div class="col-3">.col-3</div>
    <div class="col-9">.col-9</div>

    <div class="col-6">.col-6</div>
    <div class="col-6">.col-6</div>
</div>

Créez le fichier grid.css :

.flex-grid {
  display: flex;
  flex-flow: wrap;
}

.col-1 {flex: 0 0 8.3333%}
.col-2 {flex: 0 0 16.6666%}
.col-3 {flex: 0 0 25%}
.col-4 {flex: 0 0 33.3333%}
.col-5 {flex: 0 0 41.6666%}
.col-6 {flex: 0 0 50%}
.col-7 {flex: 0 0 58.3333%}
.col-8 {flex: 0 0 66.6666%}
.col-9 {flex: 0 0 75%}
.col-10 {flex: 0 0 83.3333%}
.col-11 {flex: 0 0 91.6666%}
.col-12 {flex: 0 0 100%}

[class*="col-"] {
  margin: 0 0 10px 0;

  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;
}

@media (max-width: 400px) {
  .flex-grid {
    display: block;
  }
}

J'ai créé un exemple (jsfiddle)

Essayez de redimensionner la fenêtre sous 400px, c'est réactif !!

Moshe Quantz
la source
Dans cette solution, les éléments sont ensemble, l'idée est d'avoir un long espace vide entre eux.
Juanma Menendez
2

Une autre solution possible qui ne nécessite pas d'ajouter de balisage supplémentaire consiste à ajouter une marge dynamique pour séparer les éléments.

Dans le cas de l'exemple, cela peut être fait à l'aide de calc(), simplement en ajoutant margin-leftet margin-rightà l'élément 3n + 2 (2, 5, 8)

.item:nth-child(3n+2) {
  background: silver;
  margin: 10px calc(50% - 175px);
}

Exemple d'extrait

.container {
  background: tomato;
  display: flex;
  flex-flow: row wrap;
  align-content: space-between;
  justify-content: space-between;
}
.item {
  width: 100px;
  height: 100px;
  background: gold;
  border: 1px solid black;
  font-size: 30px;
  line-height: 100px;
  text-align: center;
  margin: 10px;
}
.item:nth-child(3n+2) {
  background: silver;
  margin: 10px calc(50% - 175px);
}
<div class="container">
  <div class="item">1</div>
  <div class="item">2</div>
  <div class="item">3</div>
  <div class="item">4</div>
  <div class="item">5</div>
  <div class="item">6</div>
  <div class="item">7</div>
  <div class="item">8</div>
  <div class="item">9</div>
  <div class="item">10</div>
</div>

Juanma Menendez
la source
1
Cela mérite un vote. L'utilisation d'une combinaison de flex et de marge est un moyen très simple de prendre en charge les sauts de ligne. Cela fonctionne également très bien avec calccomme indiqué dans cette réponse.
stwilz
J'aime mieux cela, juste margin-right: 1pxl'article, et cela fera commencer l'article suivant à une nouvelle ligne.
arvil
0

Pour les questions futures, il est également possible de le faire en utilisant la floatpropriété et en l'effaçant dans chacun des 3 éléments.

Voici un exemple que j'ai fait.

.grid {
  display: inline-block;
}

.cell {
  display: inline-block;
  position: relative;
  float: left;
  margin: 8px;
  width: 48px;
  height: 48px;
  background-color: #bdbdbd;
  font-family: 'Helvetica', 'Arial', sans-serif;
  font-size: 14px;
  font-weight: 400;
  line-height: 20px;
  text-indent: 4px;
  color: #fff;
}

.cell:nth-child(3n) + .cell {
  clear: both;
}
<div class="grid">
  <div class="cell">1</div>
  <div class="cell">2</div>
  <div class="cell">3</div>
  <div class="cell">4</div>
  <div class="cell">5</div>
  <div class="cell">6</div>
  <div class="cell">7</div>
  <div class="cell">8</div>
  <div class="cell">9</div>
  <div class="cell">10</div>
</div>

Gabriel
la source
3
le problème ici est l'OP a déclaré que la solution doit utiliser flexbox ou display: flex;nondisplay: inline-block;
bafromca
1
vous pouvez écrire comme à la .cell:nth-child(3n + 1)place
Si7ius
-1

J'ai essayé plusieurs réponses ici, et aucune n'a fonctionné. Ironiquement, ce qui a fonctionné était l'alternative la plus simple à celle que l' <br/>on pourrait essayer:

<div style="flex-basis: 100%;"></div>

ou vous pouvez également faire:

<div style="width: 100%;"></div>

Placez-le où vous voulez une nouvelle ligne. Il semble fonctionner même avec des adjacents <span>, mais je l'utilise avec des adjacents <div>.

Andrew
la source
2
Les divs à 100% de largeur sont la première solution donnée dans la réponse acceptée.
TylerH
1
C'est vrai, en quelque sorte. Ils sont méprisés pour une mauvaise raison (moche, vraiment?). Aussi, ma réponse a flex-basis.
Andrew
-4

.container {
  background: tomato;
  display: flex;
  flex-flow: row wrap;
  align-content: space-between;
  justify-content: space-between;
}

.item {
  width: 100px;
  height: 100px;
  background: gold;
  border: 1px solid black;
  font-size: 30px;
  line-height: 100px;
  text-align: center;
  margin: 10px;
}
<div class="container">
  <div>
    <div class="item">1</div>
    <div class="item">2</div>
    <div class="item">3</div>
  </div>
  <div>
    <div class="item">4</div>
    <div class="item">5</div>
    <div class="item">6</div>
  </div>
  <div>
    <div class="item">7</div>
    <div class="item">8</div>
    <div class="item">9</div>
  </div>
  <div class="item">10</div>
</div>

vous pouvez essayer d'envelopper les éléments dans un élément dom comme ici. Avec cela, vous n'avez pas besoin de connaître beaucoup de CSS simplement avoir une bonne structure résoudra le problème.

Naseeruddin VN
la source
1
Vous pouvez rendre le conteneur normal display: blocket créer ces nouvelles flexbox de divs de niveau 2. Cela fonctionne pour les lignes. Remplacez les divs par des étendues lorsque vous utilisez le mode colonne.
jiggunjer