如何在多行flexbox布局中指定换行?

382

在多行 flexbox 中有没有一种方法可以进行换行?

例如,在此 CodePen中每3个项目后进行换行。

.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>

喜欢

.item:nth-child(3n){
  /* line-break: after; */    
}

1
我曾经遇到过同样或非常相似的问题;我想要每四个项目就换行,所以我只需将每个 flex 项目的宽度设置为 25vw(或 25%)。因此,在您的情况下,对于每三个项目,您将使用 33.3vw(或 33.3%)。这对我想要的效果完美地起作用了。如果有人正在寻找更简单的方法,这可能会对他们有所帮助。 - Ben Clarke
相关链接:https://dev59.com/FW445IYBdhLWcg3w9O7R - Phrogz
10个回答

437

最简单和最可靠的解决方案是在正确的位置插入弹性项目。如果它们足够宽(width: 100%),它们将强制换行。

.container {
  background: tomato;
  display: flex;
  flex-flow: row wrap;
  align-content: space-between;
  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(4n - 1) {
  background: silver;
}
.line-break {
  width: 100%;
}
<div class="container">
  <div class="item">1</div>
  <div class="item">2</div>
  <div class="item">3</div>
  <div class="line-break"></div>
  <div class="item">4</div>
  <div class="item">5</div>
  <div class="item">6</div>
  <div class="line-break"></div>
  <div class="item">7</div>
  <div class="item">8</div>
  <div class="item">9</div>
  <div class="line-break"></div>
  <div class="item">10</div>
</div>

但这样很丑不够语义化。相反,我们可以在 flex 容器内生成伪元素,并使用 order 将它们移到正确的位置。

.container {
  background: tomato;
  display: flex;
  flex-flow: row wrap;
  align-content: space-between;
  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) {
  background: silver;
}
.container::before, .container::after {
  content: '';
  width: 100%;
  order: 1;
}
.item:nth-child(n + 4) {
  order: 1;
}
.item:nth-child(n + 7) {
  order: 2;
}
<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>

但是有一个限制:弹性容器只能有一个::before和一个::after伪元素。这意味着您只能强制换行2次。

为了解决这个问题,您可以在弹性项目中生成伪元素,而不是在弹性容器中生成。这样,您就不会被限制为2个。但是那些伪元素不会成为弹性项目,因此它们将无法强制换行。

但是幸运的是,《CSS Display L3》引入了display: contents(目前仅由Firefox 37支持):

元素本身不会生成任何盒子,但其子元素和伪元素仍然像平常一样生成盒子。为了生成盒子和布局,必须将该元素视为已用其子元素和伪元素替换在文档树中。

因此,您可以将display: contents应用于弹性容器的子元素,并将每个子元素的内容包装在一个额外的包装器中。然后,弹性项目将是那些额外的包装器和子元素的伪元素。

.container {
  background: tomato;
  display: flex;
  flex-flow: row wrap;
  align-content: space-between;
  justify-content: space-between;
}
.item {
  display: contents;
}
.item > div {
  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) > div {
  background: silver;
}
.item:nth-child(3n)::after {
  content: '';
  width: 100%;
}
<div class="container">
  <div class="item"><div>1</div></div>
  <div class="item"><div>2</div></div>
  <div class="item"><div>3</div></div>
  <div class="item"><div>4</div></div>
  <div class="item"><div>5</div></div>
  <div class="item"><div>6</div></div>
  <div class="item"><div>7</div></div>
  <div class="item"><div>8</div></div>
  <div class="item"><div>9</div></div>
  <div class="item"><div>10</div></div>
</div>

或者,根据规范的旧版本,Flexbox可以通过使用break-beforebreak-after 或它们的CSS 2.1别名来实现强制换行:

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

但是这些强制换行只在Firefox上有效,而且我认为根据当前规范它们不应该起作用。新提出的方式(尚未在任何地方实现)是使用wrap-beforewrap-after

.item:nth-child(3n) {
  wrap-after: flex; /* New proposed syntax */
}

.container {
  background: tomato;
  display: flex;
  flex-flow: row wrap;
  align-content: space-between;
  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) {
  page-break-after: always;
  break-after: always;
  wrap-after: flex;
  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>


30
因为 HTML 不应该被修改以实现样式化的目的。如果你改变主意,决定要4列而不是3列,可能需要修改大量的HTML。与 break-after 的解决方案相比,只需要在样式表中修改选择器即可。 - Oriol
3
自从分页中的内容被移除规范后,是否有可能让您的第二段代码在列方向上运行,并且不会扩展其容器的高度?我尝试了一下,将 flex-basis 设置为 100% / 元素,但是它会拉伸高度。 - Lucent
1
你的第二个建议在IE10中似乎不起作用。IE11和我测试过的所有其他浏览器都没问题,但是IE10忽略了伪元素的顺序。 - Alex Podworny
1
唯一优雅的解决方案(最后一个)适用于Firefox。在其他任何浏览器中,您都需要使用伪元素来瞎搞。 - Quolonel Questions
1
gap:20px 添加到您的 .container 中,会产生另一个头疼问题:额外的元素会产生不必要的间隙。 - Artyom Shegeda
显示剩余7条评论

129
从我的角度来看,使用
元素作为伸缩项目之间的换行符
更具语义学意义。

.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>

在Chrome 66、Firefox 60和Safari 11中进行测试。


20
我也是这样做的,效果很好。添加 "hr { flex-basis: 100%; height: 0; margin: 0; border: 0; }" 可以使换行无缝连接。 - Besworks
6
我喜欢这种方式。注意:当使用 gap: 10px; 时,行之间的距离实际上是 20px。为了解决这个问题,请指定一半大小的行间距:gap: 5px 10px; - CuddleBunny
1
@Besworks:应该将border设置为none,而不是0 - Mark
@mark,border:0;border:none; 同样有效。参见:https://dev59.com/zHA85IYBdhLWcg3wJf8a - Besworks
是的,这对我的特定用例起作用了。 - alextrastero

36

@Oriol有一个很棒的答案,可惜截至到2017年10月,无论是display:contents还是page-break-after都没有得到广泛支持,更确切地说,仅有火狐浏览器支持此功能而其他浏览器则不支持。因此,我考虑了下面这种“hack”的方法,相比每3个元素就硬编码加断行符,这种方法更容易实现页面的移动端适配。

如前所述,这是一种hack方法,它的缺点是需要添加许多额外的元素,但它可以跨浏览器运行,甚至在过时的IE11上也能正常工作。

这种“hack”方法简单地在每个div后面添加一个设置为display:none的附加元素,然后使用CSS的nth-child来决定哪个元素应该显示,从而强制换行,像这样:

.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>


2
我还发现"display:contents"和"page-break-after"方法不起作用,只能采用这种"hack"方法。这被报告为Chrome的一个bug,并标记为"WontFix"(请参见https://bugs.chromium.org/p/chromium/issues/detail?id=473481),并解释说:"根据CSS工作组的说法,目前没有一种通过CSS强制在flex box中换行的方法。" - Martin_W
你可以使用选择器.container>p来减少一点混乱。然后所有那些<p></p>标签就不需要class属性了。当然,这并不是_重要的_。只是我的懒惰大脑在你聪明的解决方案中找到了一个微小的、节省空间的调整。当然,它也依赖于用户没有其他直接子元素为.container div的<p>标签。从技术上讲,你也可以对所有其他<div>子元素做同样的事情,但是在.container中有其他<div>比有<p>更有可能,所以在那里可能不是一个明智的举动。 - Steve

20
你想要一个语义化的换行符吗?那么考虑使用<br>。W3Schools可能会告诉你BR只适用于写诗(我的即将到来),但你可以更改其样式,使其表现为100%宽度的块元素,从而将你的内容推到下一行。如果“br”表示换行,则我认为它比使用hr或100%div更合适,并且使HTML更易读。在需要换行的地方插入<br>并像这样设置样式。
 // Use `>` to avoid styling `<br>` inside your boxes 
 .container > br 
 {
    width: 100%;
    content: '';
 }

你可以使用媒体查询禁用<br>, 通过将display:设置为适当的blocknone(我已经包含了一个示例,但将其注释掉了)。
如果需要,您可以使用order:来设置顺序。
您可以添加任意多个,具有不同的类或名称 :-)

.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>


不必局限于W3Schools的内容:

enter image description here


一种技术的扩展是在每两个项目后面放置<br class="2col">,在每三个项目后面放置<br class="3col">。然后将类cols-2应用于容器,并创建CSS仅对该列数启用适当的换行符。例如:br { display: none; } .cols-2 br.2col { display: block; } - Simon_Weaver
1
不,br 不是用于换行 元素 的,而是用于 文本 的:https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/br ... https://dev59.com/bm865IYBdhLWcg3wM7q0 - Asons
3
我会更改措辞,以免将其呈现为完美的解决方案,但在某些情况下,我认为这不比使用其他div或伪元素方案更糟糕。也许我现在会写一首诗来表达这个想法。 - Simon_Weaver
8
请记住,您发布的是W3Schools的打印版,而不是W3C,它们之间没有关联。 - Edu Ruiz
@Simon_Weaver 引用w3schools会影响你的分数。 - neoswf
显示剩余3条评论

9

我认为传统的方式非常灵活且相当易于理解:

标记语言

<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>

创建 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;
  }
}

我创建了一个示例(jsfiddle)

尝试在窗口大小小于400像素时进行调整,它是响应式的!


在这个解决方案中,元素是在一起的,想法是在它们之间留有一个长的空白空间。 - Juanma Menendez

9

我只是想在这里补充一下答案,提醒大家——在适当的情况下——有时候不需要过度思考问题。你想要的可能可以使用 flex:wrap max-width 来实现,而不是用:nth-child

ul {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  max-width: 420px;
  list-style-type: none;
  background-color: tomato;
  margin: 0 auto;
  padding: 0;
}

li {
  display: inline-block;
  background-color: #ccc;
  border: 1px solid #333;
  width: 23px;
  height: 23px;
  text-align: center;
  font-size: 1rem;
  line-height: 1.5;
  margin: 0.2rem;
  flex-shrink: 0;
}
<div class="root">
  <ul>
    <li>A</li>
    <li>B</li>
    <li>C</li>
    <li>D</li>
    <li>E</li>
    <li>F</li>
    <li>G</li>
    <li>H</li>
    <li>I</li>
    <li>J</li>
    <li>K</li>
    <li>L</li>
    <li>M</li>
    <li>N</li>
    <li>O</li>
    <li>P</li>
    <li>Q</li>
    <li>R</li>
    <li>S</li>
    <li>T</li>
    <li>U</li>
    <li>V</li>
    <li>W</li>
    <li>X</li>
    <li>Y</li>
    <li>Z</li>
  </ul>
</div>

https://jsfiddle.net/age3qp4d/


5
另一种可能的解决方案不需要添加额外的标记,而是添加一些动态边距来分隔元素。
在这个例子中,可以通过使用 calc() 来实现,只需将 margin-leftmargin-right 添加到3n+2元素(2, 5, 8)。
.item:nth-child(3n+2) {
  background: silver;
  margin: 10px calc(50% - 175px);
}

片段示例

.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>


1
这值得一票。使用flex和margin的组合是支持换行的一种非常简单的方式。它还可以很好地与calc一起使用,正如在这个答案中所概述的那样。 - stwilz
我更喜欢这种方式,只需将项目的 margin-right: 1px,下一个项目就会开始在新行上。 - arvil
@arvil,你能发一下解决方案吗? - Alex

1
使用 display: flex; flex-direction: column;

-3

对于未来的问题,也可以使用float属性,并在每3个元素中清除它。

这是我制作的一个示例。

.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>


3
问题在于原帖中明确要求使用flexbox或display: flex;来解决问题,而不能使用display: inline-block;。需要注意的是,翻译过程中不得添加解释性内容。 - bafromca
1
你可以写成.cell:nth-child(3n + 1) - Si7ius

-6

.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>

你可以尝试像这样将项目包装在一个DOM元素中。这样,你就不必了解太多的CSS,只要有一个良好的结构就可以解决问题。

1
你可以将容器变成普通的 display: block,并将那些新的第二层 div 变成 flexbox。这适用于行。当使用列模式时,请使用 spans 替换 divs。 - jiggunjer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接