flex-wrap:一行,或者需要几行就几行
默认情况下,flex 容器把所有项目放在同一行(flex-wrap: nowrap),并收缩它们以适应宽度。flex-wrap: wrap 则让项目保持自己的尺寸,放不下时换到下一行:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>flex-wrap</title>
<style>
body { font-family: system-ui, sans-serif; font-size: 13px; }
.box { display: flex; gap: 8px; width: 340px; padding: 8px; background: #e5e7eb; margin-bottom: 12px; }
.item { width: 100px; height: 36px; background: #2563eb; color: white; }
.wrap { flex-wrap: wrap; }
</style>
</head>
<body>
<code>flex-wrap: nowrap (default)</code>
<div class="box"><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div></div>
<code>flex-wrap: wrap</code>
<div class="box wrap"><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div><div class="item"></div></div>
<script>
document.querySelectorAll('.box').forEach((box) => {
box.children[0].textContent = Math.round(box.children[0].getBoundingClientRect().width) + 'px wide';
});
</script>
</body>
</html>
每个项目需要 100px,五个项目放在一个 340px 宽的盒子里。五个项目加上四个 8px 的间距需要 532px,所以在不换行时,每个项目被挤到约 62px,因为 flex 项目默认会收缩。设置 wrap 后,它们保持 100px:第一行放得下三个,另外两个移到第二行。
flex-wrap 的取值
| 值 | 效果 |
|---|
nowrap(默认值) | 只有一行。项目会收缩,无法再收缩时就溢出容器 |
wrap | 放不下的项目在下方开始新的一行 |
wrap-reverse | 同上,但新行向上堆叠:第一行在最下面 |
看项目如何换行
拖动滑块改变容器的宽度。读数会统计行数,并显示每行有几个项目:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Wrap slider</title>
<style>
body { font-family: system-ui, sans-serif; font-size: 14px; }
#box { display: flex; flex-wrap: wrap; gap: 8px; padding: 8px; background: #e5e7eb; margin: 10px 0; }
.item { width: 90px; height: 40px; background: #16a34a; color: white; display: flex; align-items: center; justify-content: center; }
</style>
</head>
<body>
<label>container width <input id="w" type="range" min="120" max="620" value="400"> <span id="wv"></span></label>
<div id="box"></div>
<p id="out"></p>
<script>
const box = document.getElementById('box');
for (let i = 1; i <= 6; i++) box.insertAdjacentHTML('beforeend', '<div class="item">' + i + '</div>');
const update = () => {
const w = document.getElementById('w').value;
box.style.width = w + 'px';
document.getElementById('wv').textContent = w + 'px';
const lines = {};
[...box.children].forEach((c) => { const top = Math.round(c.getBoundingClientRect().top); lines[top] = (lines[top] || 0) + 1; });
const counts = Object.values(lines);
document.getElementById('out').textContent = counts.length + ' line(s): ' + counts.join(' + ') + ' items';
};
document.getElementById('w').addEventListener('input', update);
update();
</script>
</body>
</html>
在 400px 时,四个 90px 的项目加上三个 8px 的间距(384px)能放进一行,所以结果是 4 + 2。往 620px 拖,六个项目都能放进一行;往 120px 拖,每个项目都各占一行。
flex-direction:行还是列
flex-direction 设置项目排列的方向,也就是主轴:
| 值 | 项目排列方式 | 第一个项目 |
|---|
row(默认值) | 并排,沿书写方向 | 在行的开头 |
row-reverse | 并排,方向相反 | 在行的末尾 |
column | 堆叠,从上到下 | 在最上面 |
column-reverse | 堆叠,从下到上 | 在最下面 |
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>flex-direction</title>
<style>
body { font-family: system-ui, sans-serif; font-size: 13px; }
.grid { display: flex; flex-wrap: wrap; gap: 16px; }
.box { display: flex; gap: 4px; padding: 6px; background: #e5e7eb; width: 160px; height: 110px; margin-top: 4px; }
.box div { background: #2563eb; color: white; padding: 4px 10px; }
</style>
</head>
<body>
<div class="grid">
<div><code>row</code><div class="box" style="flex-direction: row"><div>1</div><div>2</div><div>3</div></div></div>
<div><code>row-reverse</code><div class="box" style="flex-direction: row-reverse"><div>1</div><div>2</div><div>3</div></div></div>
<div><code>column</code><div class="box" style="flex-direction: column"><div>1</div><div>2</div><div>3</div></div></div>
<div><code>column-reverse</code><div class="box" style="flex-direction: column-reverse"><div>1</div><div>2</div><div>3</div></div></div>
</div>
</body>
</html>
在两个 row 盒子中,项目被拉伸到盒子的全部高度,因为 align-items: stretch 作用于行的交叉方向。在 column 盒子中,出于同样的原因,项目被拉伸到全部宽度。轴转向了,所有对齐属性也跟着转向(justify-content 页面详细展示了这一点)。
flex-flow:一行写完两个属性
flex-flow 是先写 flex-direction、再写 flex-wrap 的简写:
.cards { flex-flow: row wrap; } /* flex-direction: row; flex-wrap: wrap; */
.stack { flex-flow: column nowrap; } /* flex-direction: column; flex-wrap: nowrap; */
用 wrap 实现不需要媒体查询的响应式卡片
用 flex: 1 1 180px 给每个项目一个初始尺寸,并让这一行可以换行。每个项目会增长以填满所在的行,当一行再也放不下一个 180px 的项目时,下一个项目就会换行:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Wrapping cards</title>
<style>
body { font-family: system-ui, sans-serif; }
.cards { display: flex; flex-wrap: wrap; gap: 12px; }
.card {
flex: 1 1 180px; /* grow, shrink, start at 180px */
padding: 14px;
border: 1px solid #e5e7eb;
border-radius: 8px;
background: #f9fafb;
}
</style>
</head>
<body>
<div class="cards">
<div class="card">One</div>
<div class="card">Two</div>
<div class="card">Three</div>
<div class="card">Four</div>
<div class="card">Five</div>
</div>
</body>
</html>
在大约 800px 宽的预览中,第一行能放下三张卡片(每张约 253px),最后两张共用第二行,每张增长到约 386px。这就是 flexbox 的工作方式:每一行各自分配自己的空间,所以较短的最后一行中的卡片会比上面的更宽。如果你希望最后一张卡片和其他卡片一样宽,就改用 grid 加 repeat(auto-fill, minmax(180px, 1fr))(见 grid 页面)。
align-content:设置行与行之间的间距
当容器换行并且比各行加起来更高时,align-content 决定各行放在哪里。它接受和 justify-content 相同的值,另外还有 stretch:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>align-content</title>
<style>
body { font-family: system-ui, sans-serif; font-size: 13px; }
.box { display: flex; flex-wrap: wrap; gap: 6px; width: 200px; height: 160px; background: #e5e7eb; }
.box div { width: 60px; height: 30px; background: #f59e0b; }
.row { display: flex; gap: 16px; }
</style>
</head>
<body>
<div class="row">
<div><code>align-content: flex-start</code><div class="box" style="align-content: flex-start"><div></div><div></div><div></div><div></div><div></div></div></div>
<div><code>align-content: space-between</code><div class="box" style="align-content: space-between"><div></div><div></div><div></div><div></div><div></div></div></div>
</div>
</body>
</html>
在第一个盒子中,两行都位于顶部。在第二个盒子中,第一行在顶部,第二行在底部,所有多余的高度都在两行之间。
反向排列与无障碍
row-reverse、column-reverse 和 wrap-reverse 只改变项目绘制的位置。屏幕阅读器和 Tab 键仍然按照 HTML 中的顺序,所以键盘用户浏览项目的方向会和他看到的方向相反。反向排列只用于视觉效果;当顺序有实际含义时,请在 HTML 中调整顺序。
常见错误
- 项目收缩而不是换行。 带
flex: 1 的项目从 0 的 flex-basis 开始,会挤进同一行。给它们一个真实的基准尺寸,例如 flex: 1 1 200px。
- 把
flex-wrap 写在项目上。 它应该写在 flex 容器上。
- 用
align-items 设置换行后各行的间距。 它对齐的是每行内的项目;各行本身要用 align-content 移动。
- 最后孤零零的一个项目被拉伸到整行。 这是单个项目所在行上的
flex-grow 造成的。如果每张卡片都必须一样宽,请用 grid。
- 以为
flex-direction: column 会填满高度。 列方向的容器只和内容一样高,除非你给它设置 height,所以 justify-content 没有空间可以发挥作用。