Skip to content
This page has been auto-translated and may contain errors.View in English

Flexbox

docs.scrimba.com

你想要三个按钮排成一行,间距均匀,高度相同,并且不管每个按钮包含多少文本都能在垂直方向上居中。在过去,这意味着使用浮动、清除浮动和大量的猜测。Flexbox 就是为了这个目的而构建的工具:你告诉一个容器以行的方式排列其子元素,然后沿着两个轴线调整它们,直到它们位于你想要的位置。网络上大多数日常布局,导航栏、工具栏、卡片行,底层都是一个 flex 容器。

Flex 容器

Flexbox 从父元素上的一行代码开始。在容器上设置 display: flex,其直接子元素就会排成一行,并排放置。该容器现在是一个 flex 容器,其中的每个子元素都成为一个 flex 项目。

css
.nav {
  display: flex;    /* 子元素现在排成一行 */
}

项目沿两个方向排列。主轴从左到右运行,是项目排列的方向。交叉轴从上到下运行,横穿这一行。你将沿着两个轴线调整项目,所以现在想象它们是有帮助的:主轴是项目坐在的线,交叉轴是横穿它的方向。

display: flex 将一个元素变成 flex 容器,其直接子元素变成 flex 项目。只有直接子元素参与;任何嵌套更深的元素由其自身的父元素排列。默认情况下,项目排成一行,从左到右,每个项目收缩到其内容的宽度。

css
.nav {
  display: flex;    /* 直接子元素变成一行 flex 项目 */
}

整个模型运行在两个轴线上。主轴是项目流动的方向,默认为一行,所以从左到右。交叉轴垂直于它,从上到下运行。你接触的每个对齐属性都作用在其中一个轴线上,所以保持两个轴线清晰是使其余部分得以正确运作的关键。

display: flex 给一个元素一个 flex 格式化上下文:其直接子元素变成 flex 项目,由 flex 算法而不是正常流排列。只有直接子元素参与;文本运行和嵌套后代在它们下方处理。这与阻止 margin 折叠 的边界相同,这是 flex 布局的行为比它们替代的浮动清除时代更可预测的原因之一。

css
.nav {
  display: flex;    /* 建立一个 flex 格式化上下文 */
}

两个轴线管理一切。主轴flex-direction 设置(默认为一行,所以从 inline-start 到 inline-end),交叉轴垂直于它。这种区分不是表面上的:justify-content 总是沿主轴作用,align-items 总是沿交叉轴作用,所以当你将 flex-direction 翻转为 column 时,这两个属性会交换它们推动的方向。按轴线而不是按"水平"和"竖直"命名布局是使其在方向改变时保持一致的原因。

css
.nav {
  display: flex;
}

一行声明,任何块的子元素都停止上下堆叠并开始排成一行。本章中的其他一切都是关于在项目进入容器后对其进行调整。

JunoFlex 容器 在父元素上放置 display: flex,其直接子元素会排成一行。该父元素是 flex 容器,子元素是 flex 项目。项目沿两个方向移动:主轴是它们坐在的线,交叉轴在其上运行,你将在两个轴线上推动项目。
JunoFlex 容器display: flex 使一个容器将其直接子元素排列成一行 flex 项目。只有直接子元素参与,不是任何嵌套更深的元素。在你脑子里保持两个轴线清晰:主轴是流动方向,交叉轴是垂直的,每个对齐属性作用在其中一个轴线上。
JunoFlex 容器display: flex 开始一个 flex 格式化上下文,所以直接子元素由 flex 算法排列,margin 在其内部停止折叠。按轴线而不是水平和竖直来命名布局,因为 justify-content 跟踪主轴,align-items 跟踪交叉轴。将 flex-direction 翻转为 column,这两个就交换了它们推动的方向。

沿轴线对齐

一旦你的项目排成一行,两个属性就可以移动它们。justify-content 沿主轴间隔项目,即它们排列的方向。align-items 沿交叉轴移动它们,在行内上下移动。

css
.nav {
  display: flex;
  justify-content: space-between;   /* 沿行推开项目 */
  align-items: center;              /* 在竖直中间对齐它们 */
}

justify-content 的常见值是 flex-start(在左边聚集)、center(在中间聚集)、flex-end(在右边聚集)、space-between(项目之间有间隙,端边无间隙)和 space-around(每个项目两边有间隙)。对于 align-itemscenter 是你最常使用的。

要同时居中某物,这是每个人先学到的移动方式:将两者都设置为 center

css
.hero {
  display: flex;
  justify-content: center;   /* 沿主轴居中 */
  align-items: center;       /* 沿交叉轴居中 */
}

两个属性处理对齐,一个轴线一个。justify-content 沿主轴分布项目,align-items 在交叉轴上定位它们。因为它们拥有不同的轴线,它们永远不会相互冲突,它们指向垂直的方向。

css
.nav {
  display: flex;
  justify-content: space-between;   /* 沿主轴展开项目 */
  align-items: center;              /* 在交叉轴上居中它们 */
}

对于 justify-content,值得知道的值是 flex-startcenterflex-endspace-between(项目之间间隙相等,与边缘齐平)和 space-around(每个项目周围有相等的空间,所以端边的间隙是中间的一半)。对于 align-items,默认值是 stretch,它使每个项目填满容器的高度,这就是为什么一行卡片即使内容不同也会显示相同的高度。设置 align-items: center,它们就会收缩回其内容高度并坐在中间。

在两个轴线上居中是让 flexbox 著名的属性对:

css
.hero {
  display: flex;
  justify-content: center;   /* 主轴 */
  align-items: center;       /* 交叉轴 */
}

对齐按轴线清晰划分。justify-content 沿主轴分布自由空间,align-items 设置每个项目的默认交叉轴位置。它们不能冲突,因为它们作用在垂直的轴线上,但它们都取决于哪个方向是"主",所以 flex-direction: column 会交换每一个控制的方向。

css
.nav {
  display: flex;
  justify-content: space-between;   /* 自由空间在项目之间分布 */
  align-items: center;              /* 每个项目在交叉轴上居中 */
}

让人困惑的区别是,justify-content 只有在主轴上有剩余空间时才有任何作用。它分布自由空间,所以如果项目已经填满容器(或溢出),space-betweencenter 看起来是一样的,因为没有什么可分布的。相比之下,align-items 在容器的交叉大小内独立定位每个项目,其初始值是 stretch:没有固定交叉轴大小的项目会增长以填满容器。等高卡片行是 align-items: stretch 执行其默认工作。 用下面尺寸部分中提到的 align-self 按项目覆盖它。

css
.hero {
  display: flex;
  justify-content: center;   /* 主轴居中 */
  align-items: center;       /* 交叉轴居中,覆盖 stretch 默认 */
}

两个项目和容器上的 min-height,两者都设置为 center,你已经解决了花了一代开发者用绝对定位和负 margin 来伪造的居中问题。

Juno沿轴线对齐justify-content 沿主轴间隔项目,align-items 沿交叉轴移动它们。第一个要记的值是 centerspace-betweenflex-start。将两者都设置为 center,无论里面是什么都坐在死中心,这是你会一遍遍使用的布局移动。
Juno沿轴线对齐 一个属性一个轴线:主轴上的 justify-content,交叉轴上的 align-items。默认 align-items: stretch 就是为什么一行卡片会自动显示相同的高度而无需你请求。两者都设置为 center 是值得记住的居中任何东西的移动。
Juno沿轴线对齐justify-content 分布剩余的主轴空间,所以如果项目已经填满行它就什么都不做,这解释了很多"为什么这不居中"的时刻。align-items 在交叉轴上定位每个项目,默认为 stretch,为你免费提供等高行。翻转 flex-direction,记住两个属性都交换它们作用的轴线。

方向和换行

默认情况下,flex 容器以行方式排列项目。使用 flex-direction 改变这个。将其设置为 column,项目就会从上到下堆叠,而不是并排放置。

css
.sidebar {
  display: flex;
  flex-direction: column;   /* 项目现在竖直堆叠 */
}

当一行中的项目用尽空间时,它们会变得越来越薄。为了让它们落到新的一行上,加上 flex-wrap: wrap

css
.card-row {
  display: flex;
  flex-wrap: wrap;   /* 不适合的项目移动到下一行 */
}

对于项目之间的空间,使用 gap。它在每个项目之间添加一致的间隙,而不在外边缘添加空间。

css
.card-row {
  display: flex;
  gap: 16px;   /* 每个项目之间 16px */
}

flex-direction 设置主轴。默认 row 从左到右运行;column 将主轴变成竖直,所以项目从上到下堆叠,重要的是,justify-content 现在控制竖直间隔,而 align-items 控制水平。还有 row-reversecolumn-reverse,如果你需要顺序翻转。

css
.sidebar {
  display: flex;
  flex-direction: column;   /* 主轴现在是竖直的 */
  justify-content: center;  /* 这意味着这居中竖直 */
}

默认情况下,flex 行永远不会断裂:项目收缩以适应而不是换行。设置 flex-wrap: wrap,不适合的项目移动到新行,这是不使用媒体查询实现响应式卡片网格的原因。

css
.card-row {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;   /* gap 在两个轴线上工作:行和列之间各 16px */
}

使用 gap 做间隔,不是项目上的 margin。gap 只在项目之间应用,从不在外边缘,所以你永远不必去掉最后一个子元素上的后续 margin。

flex-direction 定义主轴,每个轴线依赖属性都跟随它。row(默认)使主轴水平;column 使其竖直,并无声地重新分配 justify-content 到竖直间隔和 align-items 到水平。-reverse 变体翻转主轴的方向,但要注意它们只在视觉上重新排序:DOM 顺序不变,所以键盘和屏幕阅读器顺序仍然遵循源,当你使用 row-reverse 时这是一个真实的可访问性考虑。

css
.sidebar {
  display: flex;
  flex-direction: column;    /* 主轴竖直;justify-content 现在居中竖直 */
  gap: 8px;
}

flex-wrap: wrap 解除单行限制。没有它,flex 容器强制所有项目在一行上,并通过收缩它们来分布任何不足;有了它,溢出主大小的项目会断裂到新行,容器变成多行。一旦换行开启,align-content(与 align-items 不同)控制在交叉轴上如何间隔。gap 设置项目之间的空间,当换行时,也在行之间,它是正确的工具:不像 margin 它不折叠任何东西,不添加边缘空间,并且读起来像一个单一间隔决定。

css
.card-row {
  display: flex;
  flex-wrap: wrap;
  gap: 16px 24px;   /* row-gap 16px,column-gap 24px */
}

这也是与 CSS grid 的边界显示的地方。Flex 换行填充一行,然后断裂,所以不同行上的项目不会对齐到列中。当你需要一个真实的二维网格,具有对齐的行和列时,那是 grid 的工作,不是 flexbox 的。

Juno方向和换行flex-direction: column 竖直堆叠项目而不是在一行中。flex-wrap: wrap 让项目在用尽空间时掉到新行,而不是被压扁。而 gap 在项目之间放置均匀的空间而不在边缘添加任何,所以在 margin 之前选择它。
Juno方向和换行flex-direction 设置主轴,切换到 column 意味着 justify-content 现在竖直间隔,这会让人惊讶一次。flex-wrap: wrap 是使卡片行不使用媒体查询就能重流的原因。使用 gap 做间隔,这样你永远不会追逐最后一个项目上的流浪 margin。
Juno方向和换行flex-direction 定义主轴,每个基于轴线的属性都跟随它,而 reverse 值在视觉上重新排序但单独保留 DOM 和焦点顺序,所以小心使用它们。flex-wrap 将容器变成多行并将行间隔交给 align-content。当换行的项目需要对齐到真实的列中时,那是 grid,不是 flexbox。

灵活的项目尺寸

flexbox 的意义在于项目可以灵活变化,增长或收缩来共享空间。你最常用的简写是项目上的 flex: 1,它告诉它增长并获得可用房间的相等份额。

css
.card {
  flex: 1;   /* 每个卡片增长以填满行的相等份额 */
}

在一行中的三个卡片上放置 flex: 1,它们将容器分成三个相等的宽度。给一个卡片 flex: 2,它获得其他卡片两倍的空间。那个单一数字是项目如何分割它们之间的剩余空间。

flex: 1 后面有三个单独的控制:项目增长多少,它收缩多少,及其起始大小。最初你很少逐个设置它们,简写覆盖常见情况,但知道它们在那里是有帮助的。

三个属性决定项目如何共享空间。flex-grow 设置项目增长多少以填满剩余空间(0 意味着不增长)。flex-shrink 设置当空间紧张时它放弃多少(1,默认,意味着它可能收缩)。flex-basis 设置项目的起始大小,在增长或收缩之前,它默认为 auto,意思是"使用内容或宽度"。

flex 简写捆绑所有三个,你实际输入的值是很少的:

css
.card {
  flex: 1;   /* 增长 1,收缩 1,基础 0:等宽列 */
}

.sidebar {
  flex: 0 0 240px;   /* 不增长,不收缩,保持 240px */
}

flex: 1 是主力:它使项目不管内容如何都将行分成相等的份额,因为 0 基础意味着它们从零开始,只由其增长因子增长。flex: 0 0 240px 是固定宽度的侧栏,旁边是 flex: 1 的主区域,一个你会不断构建的布局。

项目尺寸通过容器按顺序评估的三个属性来解决。flex-basis 是项目的起始主轴大小,在分布任何自由空间之前获取;其默认 auto 延迟到项目的 width(或内容大小)。然后容器测量基础值之和与其自身大小之间的差异。如果有自由空间,它按照每个项目的 flex-grow 的比例分配。如果有不足,它按照每个项目的 flex-shrink 的比例乘以其基础而被收回。

css
/* flex: 1 扩展为 flex-grow: 1, flex-shrink: 1, flex-basis: 0% */
.card {
  flex: 1;   /* 基础 0,所以项目忽略内容宽度并按增长因子分割空间 */
}

微妙的部分是 flex-basis 对比 width,因为它们相互影响。flex-basis: 0(如在 flex: 1 中)使项目忽略其内容并只按增长因子分割空间,给出真正的相等列。flex-basis: auto(如在 flex: auto 中,或普通 width)使每个项目从其内容或声明的宽度开始,然后分享余数,所以内容更多的项目最终更宽。选择其中一个真的是选择内容是否应该影响最终宽度。

两个生产备注。首先,flex-basis 打败 width,当两者都设置时,所以一旦项目是 flex 项目,更喜欢通过 flex 表达其大小。其次,溢出陷阱:flex 项目有默认 min-width: auto,意思是它拒绝收缩到其内容的内在大小以下,所以一个长词或溢出子元素可能会炸掉整行。修复是在应该被允许收缩的项目上设置 min-width: 0

css
.main {
  flex: 1;
  min-width: 0;   /* 让这个项目收缩到其内容以下,所以长文本截断而不是溢出 */
}

align-self 覆盖单个项目的 align-items,所以一个项目可以坐在 flex-start 而其余的居中。而工具调用:对一维布局使用 flexbox,一个导航栏,一个工具栏,项目的单一行或列。当你需要同时对齐行列时,CSS grid 是正确的工具。如果你不确定宽度如何在这一切下解决,sizing units 覆盖百分比和其他单位在这里如何行动。

css
.layout {
  display: flex;
  gap: 24px;
}
.sidebar {
  flex: 0 0 240px;   /* 固定 240px,永远不增长或收缩 */
}
.main {
  flex: 1;           /* 获取所有剩余空间 */
}

一个固定侧栏旁边是一个灵活的主列,三行意图。这一个模式,加上早前的对齐导航栏,覆盖了你将构建的大部分布局。

Juno灵活的项目尺寸 项目上的 flex: 1 告诉它增长并获得行的相等份额,所以三个有 flex: 1 的卡片出现相同的宽度。将一个调到 flex: 2,它获得两倍的空间。那个数字是 flex 项目如何分割它们之间的房间。
Juno灵活的项目尺寸flex 简写捆绑增长、收缩和基础。flex: 1 给出等宽列,因为其 0 基础使项目从零开始并均匀增长。flex: 0 0 240px 是你的固定侧栏旁边一个 flex: 1 的主区域,一个你会一遍遍选择的布局。
Juno灵活的项目尺寸flex-basis 是在空间共享前的起始大小,它在 flex 项目上打败 width,所以 flex: 1(基础 0)均匀分割空间,而 flex: auto 让内容影响宽度。注意默认 min-width: auto,它停止项目收缩到其内容以下,溢出行直到你设置 min-width: 0。使用 align-self 将一个项目剥离对齐,一旦你需要对齐的行和列就切换到 grid。