flexbox 的基本概念

弹性盒子布局模块(通常简称为 flexbox)是一种一维布局模型,用于在项目之间分配空间,并包含多种对齐功能。本文概述了 flexbox 的主要功能,我们将在本指南的其余部分中更详细地探讨这些功能。

当我们描述 flexbox 是一维时,我们指的是 flexbox 一次只处理一个维度的布局——要么是行,要么是列。这与 CSS Grid Layout 的二维模型形成对比,后者同时控制行和列。

Flexbox 的两个轴

使用 flexbox 时,你需要考虑两个轴——*主轴*和*侧轴*。主轴flex-direction 属性定义,侧轴垂直于主轴运行。我们使用 flexbox 所做的一切都与这些轴相关,因此从一开始就了解它们的工作原理是值得的。

主轴

主轴flex-direction 定义,它有四个可能的值

  • row
  • row-reverse
  • column
  • column-reverse

如果你选择 rowrow-reverse,你的主轴将沿行方向在**行内方向**上运行。

If flex-direction is set to row the main axis runs along the row in the inline direction.

选择 columncolumn-reverse,你的主轴将沿**块方向**运行,从页面的顶部到底部。

If flex-direction is set to column the main axis runs in the block direction.

侧轴

侧轴垂直于主轴。因此,如果你的 flex-direction(主轴)设置为 rowrow-reverse,则侧轴沿列向下运行。

If flex-direction is set to row then the cross axis runs in the block direction.

如果你的主轴是 columncolumn-reverse,那么侧轴沿行运行。

If flex-direction is set to column then the cross axis runs in the inline direction.

起始线和结束线

另一个重要的理解领域是 flexbox 不对文档的书写模式做任何假设。Flexbox 不仅仅假设所有文本行都从文档的左上角开始,然后向右侧运行,新行一个接一个地出现。相反,它支持所有书写模式,就像其他逻辑属性和值一样。

你可以在后面的文章中阅读更多关于 flexbox 和书写模式之间关系的信息;但是,以下描述应该有助于解释为什么我们不谈论左和右以及上和下,而是描述我们的弹性项目流动的方向。

如果 flex-directionrow,并且我正在使用英语,那么主轴的起始边将在左侧,结束边将在右侧。

Working in English the start edge is on the left.

如果我使用阿拉伯语,那么我的主轴的起始边将在右侧,结束边将在左侧。

The start edge in a RTL language is on the right.

在这两种情况下,侧轴的起始边都在弹性容器的顶部,结束边都在底部,因为这两种语言都具有水平书写模式。

过了一段时间,思考起始和结束而不是左和右会变得很自然,并且当你处理其他遵循相同模式的布局方法(例如 CSS Grid Layout)时,这将对你很有用。

弹性容器

使用 flexbox 进行布局的文档区域称为**弹性容器**。要创建弹性容器,请将其display属性设置为flex。当我们这样做时,该容器的直接子元素将成为**弹性项目**。你可以使用inline flexinline-flex用于行内弹性容器,或使用block flexflex用于块级弹性容器,显式控制容器本身是以行内还是块级格式化上下文显示。

初始值

与 CSS 中的所有属性一样,定义了一些初始值,因此新弹性容器的内容将按以下方式运行

  • 项目以行显示(flex-direction 属性的默认值为 row)。
  • 项目从主轴的起始边缘开始。
  • 项目在主维度上不拉伸但可以收缩(弹性项目的 flex-grow 属性的默认值为 0,其 flex-shrink 属性的默认值为 1)。
  • 项目将拉伸以填充侧轴的大小(align-items 属性的默认值为 stretch)。
  • 弹性项目的 flex-basis 属性的默认值为 auto。这意味着,在每种情况下,它都将等于水平书写模式下的弹性项目 width,以及垂直书写模式下的弹性项目 height。如果相应的 width/height 也设置为 auto,则改用 flex-basiscontent 值。
  • 所有项目都将在一行中(flex-wrap 属性的默认值为 nowrap),如果它们的组合 width/height 超过了包含元素的 width/height,它们将溢出其容器。

结果是你的项目都会排成一排,以内容的大小作为它们在主轴上的大小。如果项目数量超出容器所能容纳的范围,它们将不会换行,而是会溢出。如果有些项目比其他项目高,所有项目都将沿着侧轴的整个长度拉伸。

你可以在下面的实时示例中看到它的样子。点击“播放”在 MDN Playground 中打开示例,并编辑项目或添加新项目以尝试 flexbox 的初始行为

html
<div class="box">
  <div>One</div>
  <div>Two</div>
  <div>Three <br />has <br />extra <br />text</div>
</div>
css
.box > * {
  border: 2px solid rgb(96 139 168);
  border-radius: 5px;
  background-color: rgb(96 139 168 / 0.2);
}

.box {
  border: 2px dotted rgb(96 139 168);
  display: flex;
}

改变 flex-direction

向弹性容器添加 flex-direction 属性可以让我们改变弹性项目的显示方向。设置 flex-direction: row-reverse 将使项目沿行显示,但起始线和结束线会交换。

如果我们将 flex-direction 更改为 column,主轴将切换,我们的项目现在以列的形式显示。设置为 column-reverse,起始线和结束线将再次交换。

下面的实时示例将 flex-direction 设置为 row-reverse。尝试其他值——rowcolumncolumn-reverse——看看内容会发生什么。

html
<div class="box">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
</div>
css
.box > * {
  border: 2px solid rgb(96 139 168);
  border-radius: 5px;
  background-color: rgb(96 139 168 / 0.2);
}

.box {
  border: 2px dotted rgb(96 139 168);
  display: flex;
  flex-direction: row-reverse;
}

使用 flex-wrap 的多行弹性容器

虽然 flexbox 是一维模型,但可以使 flex 项目在多行中换行。如果你这样做,你应该将每行视为一个新的 flex 容器。任何空间分布都将发生在每行中,而与前一行或后续行无关。

要实现换行行为,请添加属性 flex-wrap,并将其值设为 wrap。现在,如果你的项目太大而无法全部显示在一行中,它们将换到另一行。下面的实时示例包含已设置 width 的项目。项目的总宽度对于弹性容器来说太宽了。由于 flex-wrap 设置为 wrap,项目将在多行中换行。如果将其设置为 nowrap(这是初始值),它们将收缩以适应容器。它们之所以收缩,是因为它们使用了初始的 flexbox 值,包括 flex-shrink: 1,这允许项目收缩。如果项目无法收缩,或者无法收缩到足以适应的程度,使用 nowrap 将导致溢出

html
<div class="box">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
</div>
css
.box > * {
  border: 2px solid rgb(96 139 168);
  border-radius: 5px;
  background-color: rgb(96 139 168 / 0.2);
  width: 200px;
}

.box {
  width: 500px;
  border: 2px dotted rgb(96 139 168);
  display: flex;
  flex-wrap: wrap;
}

在指南 掌握弹性项目的换行 中了解更多关于换行弹性项目的信息。

flex-flow 简写

你可以将 flex-directionflex-wrap 这两个属性组合成 flex-flow 简写。

在下面的实时示例中,尝试将第一个值更改为 flex-direction 的允许值之一——rowrow-reversecolumncolumn-reverse,同时将第二个值更改为 wrapnowrap

html
<div class="box">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
</div>
css
.box > * {
  border: 2px solid rgb(96 139 168);
  border-radius: 5px;
  background-color: rgb(96 139 168 / 0.2);
  width: 200px;
}

.box {
  width: 500px;
  border: 2px dotted rgb(96 139 168);
  display: flex;
  flex-flow: row wrap;
}

应用于弹性项目的属性

为了控制每个弹性项目的行内大小,我们直接通过三个属性来控制它们

我们将在下面简要介绍这些属性,但如果你想获取更全面的信息,请查看控制主轴上弹性项目的比例指南。

在我们理解这些属性之前,我们需要考虑**可用空间**的概念。当我们改变这些弹性属性的值时,我们正在改变可用空间在项目之间的分布方式。当我们要对齐项目时,可用空间的概念也很重要。

如果一个容器宽 500 像素,里面有三个宽 100 像素的项目,那么我们布局项目所需的空间就是 300 像素。这样就剩下 200 像素的可用空间。如果我们不改变初始值,那么 flexbox 会将这些空间放在最后一个项目之后。

This flex container has available space after laying out the items.

如果我们希望项目增长并填充空间,那么我们需要一种方法来在项目之间分配剩余空间。我们应用于项目本身的 flex 属性,可以决定这些可用空间应该如何分配给兄弟弹性项目。

flex-basis 属性

flex-basis 定义了该项目的大小,就其作为可用空间留下的空间而言。此属性的初始值为 auto — 在这种情况下,浏览器会查看项目是否具有大小。在上面的示例中,所有项目的宽度都是 100 像素。这被用作 flex-basis

如果项目没有大小,则使用内容的大小作为 flex-basis。这就是为什么当我们仅在父元素上声明 display: flex 来创建弹性项目时,所有项目都排成一行,并且只占用显示其内容所需的空间。

flex-grow 属性

flex-grow 属性设置为正整数时,如果有可用空间,弹性项目可以沿主轴从其 flex-basis 增长。项目是否拉伸以占据该轴上的所有可用空间,或者仅仅是可用空间的一部分,取决于其他项目是否也允许增长以及它们的 flex-grow 属性的值。

每个具有正值的项目都会根据其 flex-grow 值占用一部分可用空间。如果我们将上面示例中的所有项目都赋予 flex-grow 值为 1,那么弹性容器中的可用空间将平均分配给我们的项目,并且它们将沿主轴拉伸以填充容器。如果我们将第一个项目赋予 flex-grow 值为 2,而其他项目各赋予 1,则总共有 4 份;第一项将获得可用空间的 2 份(在上面的示例中,200px 中的 100px),其余两项各获得 1 份(200px 中的 50px)。

flex-shrink 属性

flex-grow 属性处理在主轴上增加空间,而 flex-shrink 属性则控制如何减小空间。如果容器中没有足够的空间来布局我们的项目,并且 flex-shrink 设置为正整数,那么项目可以变得比 flex-basis 小。与 flex-grow 一样,可以分配不同的值以使一个项目比其他项目收缩得更快——设置了更高 flex-shrink 值的项目将比具有较低值的兄弟项目收缩得更快。

项目可以收缩到其 min-content 大小。在计算实际收缩量时会考虑这个最小大小,这意味着 flex-shrink 在行为上可能看起来不如 flex-grow 一致。因此,我们将在文章 控制主轴上项目的比例 中更详细地研究此算法的工作原理。

注意:flex-growflex-shrink 的这些值是比例。通常,如果我们所有项目都设置为 flex: 1 1 200px,然后希望其中一个项目以两倍的速度增长,我们会将该项目设置为 flex: 2 1 200px。但是,如果你愿意,也可以使用 flex: 10 1 200pxflex: 20 1 200px

flex 属性的简写值

你很少会看到 flex-growflex-shrinkflex-basis 属性单独使用;相反,它们被组合成 flex 简写。flex 简写允许你按此顺序设置这三个值——flex-growflex-shrinkflex-basis

下面的实时示例允许你测试 flex 简写的不同值;请记住,第一个值是 flex-grow。将其设置为正值意味着项目可以增长。第二个是 flex-shrink — 设为正值时,项目可以收缩,但前提是它们的总值溢出主轴。最后一个值是 flex-basis;这是项目作为其增长和收缩的基础值。

html
<div class="box">
  <div class="one">One</div>
  <div class="two">Two</div>
  <div class="three">Three</div>
</div>
css
.box > * {
  border: 2px solid rgb(96 139 168);
  border-radius: 5px;
  background-color: rgb(96 139 168 / 0.2);
}

.box {
  border: 2px dotted rgb(96 139 168);
  display: flex;
}

.one {
  flex: 1 1 auto;
}

.two {
  flex: 1 1 auto;
}

.three {
  flex: 1 1 auto;
}

还有一些预定义的简写值涵盖了大多数用例。你经常会在教程中看到这些,并且在许多情况下,这些就是你所需要使用的全部。预定义值如下

  • flex: initial
  • flex: auto
  • flex: none
  • flex: <positive-number>

initial 值是一个CSS 全局关键字,表示属性的初始值。设置 flex: initial 会将项目重置为三个长手属性的初始值,这与 flex: 0 1 auto 相同。flex-grow 的初始值为 0,因此项目不会超过其 flex-basis 大小。flex-shrink 的初始值为 1,因此项目在需要时可以收缩,而不是溢出。flex-basis 的初始值为 auto。项目将使用在主维度上设置的任何大小,或者它们将从内容大小获取其大小。

使用 flex: auto 与使用 flex: 1 1 auto 相同;这类似于 flex: initial,只是项目在需要时可以增长并填充容器以及收缩。

使用 flex: none 将创建完全不可伸缩的弹性项目。它就像你写了 flex: 0 0 auto。项目不能增长或收缩,并将使用 flex-basisauto 的 flexbox 进行布局。

你在教程中经常看到的简写是 flex: 1flex: 2 等等。这与分别编写 flex: 1 1 0flex: 2 1 0 等等是相同的。由于 flex-basis: 0,项目获得最小尺寸,然后按比例增长以填充可用空间。在这种情况下,flex-shrink1 是多余的,因为项目从最小尺寸开始——它们没有被赋予任何可能导致它们溢出弹性容器的尺寸。

在下面的实时示例中尝试这些简写值。

html
<div class="box">
  <div class="one">One</div>
  <div class="two">Two</div>
  <div class="three">Three</div>
</div>
css
.box > * {
  border: 2px solid rgb(96 139 168);
  border-radius: 5px;
  background-color: rgb(96 139 168 / 0.2);
}

.box {
  border: 2px dotted rgb(96 139 168);
  display: flex;
}

.one {
  flex: 1;
}

.two {
  flex: 1;
}

.three {
  flex: 1;
}

项目之间的对齐、对齐和自由空间分布

Flexbox 的一个关键特性是能够在主轴和侧轴上对齐和对齐项目,以及在弹性项目之间分配空间。请注意,这些属性设置在弹性容器上,而不是项目本身上。

align-items

align-items 属性在侧轴上对齐所有弹性项目。

此属性的初始值为 stretch,这就是为什么弹性项目默认情况下会拉伸到弹性容器的高度(如果 flex-direction 设置为 columncolumn-reverse,则为宽度)。此高度可能来自容器中最高的项目,或在弹性容器本身上设置的大小。

你可以选择将 align-items 设置为 flex-start,或简单地设置为 start,以便使项目对齐到弹性容器的起始位置;设置为 flex-end,或简单地设置为 end,以便对齐到结束位置;或者设置为 center 以便对齐到中心。在实时示例中尝试一下——我已经为弹性容器设置了高度,以便你可以看到项目如何在容器内移动。看看将 align-items 的值设置为以下值时会发生什么

  • stretch
  • flex-start
  • flex-end
  • start
  • end
  • center
  • baseline
  • last baseline
html
<div class="box">
  <div>One</div>
  <div>Two</div>
  <div>Three <br />has <br />extra <br />text</div>
</div>
css
.box > * {
  border: 2px solid rgb(96 139 168);
  border-radius: 5px;
  background-color: rgb(96 139 168 / 0.2);
}

.box {
  width: 500px;
  height: 130px;
  border: 2px dotted rgb(96 139 168);
  display: flex;
  align-items: flex-start;
}

align-items 设置在弹性容器上,并影响所有弹性项目。如果你想以不同于其他项目的方式对齐某个弹性项目,你可以在该弹性项目上设置 align-self

justify-content

justify-content 属性用于在主轴上对齐项目,即 flex-direction 设置的流向。初始值为 flex-start,它会将项目对齐到容器的起始边缘,但你也可以将值设置为 flex-end 以对齐到末尾,或者设置为 center 以对齐到中心。

你还可以使用 space-between 值,它会在项目布局后将所有剩余空间均匀地分配给项目,从而使每个项目之间都有相等的空间。要使每个项目左右(或列的上下)都有相等的空间,请使用 space-around 值。使用 space-around,项目两端都有半个空间的间距。或者,要使项目周围有相等的空间,请使用 space-evenly 值。使用 space-evenly,项目两端都有一个完整空间的间距。

在实时示例中尝试以下 justify-content

  • start
  • end
  • left
  • right
  • normal
  • flex-start
  • flex-end
  • center
  • space-around
  • space-between
  • space-evenly
  • stretch
html
<div class="box">
  <div>One</div>
  <div>Two</div>
  <div>Three</div>
</div>
css
.box > * {
  border: 2px solid rgb(96 139 168);
  border-radius: 5px;
  background-color: rgb(96 139 168 / 0.2);
}

.box {
  border: 2px dotted rgb(96 139 168);
  display: flex;
  justify-content: flex-start;
}

文章 在弹性容器中对齐项目 更深入地探讨了这些属性,以便更好地理解它们的工作原理。然而,这些基本示例在大多数用例中都很有用。

justify-items

在弹性盒布局中,justify-items 属性被忽略。

place-items 和 place-content

place-items 属性是 align-itemsjustify-items 的简写属性。如果设置在弹性容器上,它将设置对齐方式而非对齐理由,因为 justify-items 在 flexbox 中被忽略。

还有另一个简写属性,place-content,它定义了 align-contentjustify-content 属性。align-content 属性只影响换行的弹性容器,并在在弹性容器中对齐项目中讨论。

后续步骤

阅读本文后,你应对 flexbox 的基本功能有所了解。在下一篇文章中,我们将探讨 此规范如何与其他 CSS 部分相关联