Flexbox

Flexbox 是一种一维布局方法,用于在行或列中排列项目。项目会弹性(扩展)填充额外空间或收缩以适应较小的空间。本文解释了所有基础知识。

预备知识 使用 HTML 构造内容CSS 样式基础文本和字体样式基础,熟悉CSS 布局基本概念
学习成果
  • flexbox 的目的——以一维方式灵活地布局一组块级或内联元素。
  • Flex 术语——flex 容器、flex 项目、主轴和交叉轴。
  • 理解 display: flex 默认提供的内容。
  • 如何将内容换行到新行和新列。
  • flex 项目的弹性尺寸和排序。
  • 内容对齐和分散对齐。

为什么选择 flexbox?

CSS 弹性盒布局使你能够

  • 在父级中垂直居中一个块级内容。
  • 使容器的所有子元素占据可用宽度/高度的相等部分,无论可用宽度/高度是多少。
  • 即使多列布局中的所有列包含不同数量的内容,也能使它们采用相同的高度。

Flexbox 功能可能是您一维布局需求的完美解决方案。让我们深入了解一下!

注意: Scrimba 的Flexbox MDN 学习伙伴 入门 scrim 提供了一个交互式指南,涵盖了 flexbox 在 Web 上的常见程度以及为什么学习它如此重要,并引导您完成一个典型的用例,演示了 flexbox 的强大功能。

介绍一个简单示例

在本文中,您将通过一系列练习来帮助您理解 flexbox 的工作原理。要开始,您应该创建 HTML 和 CSS 的本地副本。在现代浏览器(如 Firefox 或 Chrome)中加载它,并在您的代码编辑器中查看代码。或者,点击“播放”按钮在 playground 中打开它。

html
<header>
  <h1>Sample flexbox example</h1>
</header>
<section>
  <article>
    <h2>First article</h2>
    <p>Content…</p>
  </article>
  <article>
    <h2>Second article</h2>
    <p>Content…</p>
  </article>
  <article>
    <h2>Third article</h2>
    <p>Content…</p>
  </article>
</section>
css
body {
  font-family: sans-serif;
  margin: 0;
}
header {
  background: purple;
  height: 100px;
}
h1 {
  text-align: center;
  color: white;
  line-height: 100px;
  margin: 0;
}
section {
  zoom: 0.8;
}
article {
  padding: 10px;
  margin: 10px;
  background: aqua;
}
/* Add your flexbox CSS below here */

您将看到我们有一个 <header> 元素,其中包含一个顶级标题,以及一个 <section> 元素,其中包含三个 <article>。我们将使用它们来创建一个相当标准的三列布局。

指定哪些元素以弹性盒布局

首先,我们需要选择哪些元素将作为弹性盒布局。为此,我们在您要影响的元素的父元素上设置一个特殊的 display 值。在本例中,我们要布局 <article> 元素,因此我们在 <section> 上设置它。

css
section {
  display: flex;
}

这将导致 <section> 元素成为flex 容器,其子元素成为flex 项目。这就是它的样子:

这个单一的声明为我们提供了所需的一切。令人难以置信,对吧?我们有一个多列布局,列的大小相等,并且所有列的高度都相同。这是因为赋予 flex 项目(flex 容器的子元素)的默认值是为了解决此类常见问题而设置的。

让我们回顾一下这里发生了什么。向元素添加 flexdisplay 值会使其成为 flex 容器。该容器在与页面其余部分的交互方式上显示为块级内容。当元素转换为 flex 容器时,其子元素将转换为(并布局为)flex 项目。

您可以使用外部 display(例如,display: inline flex)使容器成为内联,这会影响容器本身在页面中的布局方式。传统的 inline-flex display 值也会将容器显示为内联。在本教程中,我们将重点关注容器内容的行为方式,但如果您想了解内联与块布局的效果,可以在 display 属性页面上查看值比较

接下来的部分将更详细地解释什么是 flex 项目以及当您将元素设为 flex 容器时元素内部会发生什么。

弹性模型

当元素作为 flex 项目布局时,它们沿着两个轴布局:

Three flex items in a left-to-right language are laid out side-by-side in a flex container. The main axis — the axis of the flex container in the direction in which the flex items are laid out — is horizontal. The ends of the axis are main-start and main-end and are on the left and right respectively. The cross axis is vertical; perpendicular to the main axis. The cross-start and cross-end are at the top and bottom respectively. The length of the flex item along the main axis, in this case, the width, is called the main size, and the length of the flex item along the cross axis, in this case, the height, is called the cross size.

  • 主轴是 flex 项目布局方向上的轴(例如,横向排列成一行,或纵向排列成一列)。该轴的起点和终点分别称为主轴起点主轴终点。从主轴起点边到主轴终点边的长度是主尺寸
  • 交叉轴是垂直于 flex 项目布局方向的轴。该轴的起点和终点分别称为交叉轴起点交叉轴终点。从交叉轴起点边到交叉轴终点边的长度是交叉尺寸
  • 设置了 display: flex 的父元素(在我们的示例中是 <section>)称为flex 容器
  • 在 flex 容器内作为弹性盒布局的项称为flex 项目(在我们的示例中是 <article> 元素)。

在后续章节中请牢记这些术语。如果您对使用的任何术语感到困惑,可以随时回顾。

列或行?

Flexbox 提供了一个名为 flex-direction 的属性,它指定了主轴的方向(即 flexbox 子元素布局的方向)。默认情况下,它设置为 row,这会导致它们按照浏览器默认语言的方向(对于英语浏览器,从左到右)排列成一行。

尝试将以下声明添加到您的 <section> 规则中:

css
flex-direction: column;

您会看到这将使项目恢复为列布局,就像我们添加任何 CSS 之前一样。在继续之前,从您的示例中删除此声明。

注意:您还可以使用 row-reversecolumn-reverse 值以相反方向布局 flex 项目。也尝试一下这些值!

换行

当您的布局具有固定宽度或高度时,会出现一个问题:您的 flexbox 子元素最终会溢出其容器,从而破坏布局。在以下示例中,我们有 5 个 <article>,它们不适合,因为它们具有 400pxmin-width,因此会出现水平滚动。

在这里我们看到子元素确实溢出了它们的容器。默认情况下,如果 flex-direction 设置为 row,浏览器会尝试将所有 flex 项目放置在单行中;如果 flex-direction 设置为 column,则放置在单列中。

解决此问题的一种方法是将以下声明添加到您的 <section> 规则中:

css
section {
  flex-wrap: wrap;
}

您会看到,包含此内容后,布局看起来好多了:

我们现在有多行。每行都尽可能合理地容纳了许多 flexbox 子元素。任何溢出都向下移动到下一行。

但我们可以在这里做更多。首先,尝试将您的 flex-direction 属性值更改为 row-reverse。现在您会看到您仍然有多行布局,但它从浏览器窗口的相反角落开始并反向流动。

flex-flow 缩写

此时值得注意的是,flex-directionflex-wrap 有一个缩写:flex-flow。例如,您可以替换:

css
flex-direction: row;
flex-wrap: wrap;

with

css
flex-flow: row wrap;

flex 项目的弹性尺寸

现在让我们回到第一个示例,看看如何控制 flex 项目所占空间与其他 flex 项目相比的比例。

在本地副本中,将以下规则添加到 CSS 的底部:

css
article {
  flex: 1;
}

这是一个无单位的比例值,它决定了每个 flex 项目沿主轴占据可用空间的多少,与其他 flex 项目相比。在这种情况下,我们为每个 <article> 元素赋予相同的值(值 1),这意味着它们都将占据在设置了 padding 和 margin 等属性后剩余的空闲空间的相等部分。这个值在 flex 项目之间按比例共享:给每个 flex 项目一个值 400000 会产生完全相同的效果。

现在在前面的规则下面添加以下规则:

css
article:nth-of-type(3) {
  flex: 2;
}

现在当你刷新时,你会看到第三个 <article> 占据的可用宽度是其他两个的两倍。现在总共有四个比例单位(因为 1 + 1 + 2 = 4)。前两个 flex 项目各有一个单位,所以它们各占据可用空间的 1/4。第三个有两个单位,所以它占据可用空间的 2/4(或一半)。

你也可以在 flex 值中指定一个最小尺寸值。尝试像这样更新你现有的文章规则:

css
article {
  flex: 1 100px;
}

article:nth-of-type(3) {
  flex: 2 100px;
}

这基本上说明,“每个弹性项目将首先获得 100px 的可用空间。之后,剩余的可用空间将根据比例单位进行共享。”您将看到空间共享方式的不同。

所有 flex 项目的最小宽度均为 100 像素——使用“flex”设置。前两个 flex 项目的 flex 值为 1,第三个项目的 flex 值为 2。这将 flex 容器中的剩余空间分成 4 个比例单位。一个单位分配给前两个 flex 项目,2 个单位分配给第三个 flex 项目,使第三个 flex 项目比其他两个(宽度相同)更宽。

flexbox 的真正价值体现在其灵活性/响应性。如果您调整浏览器窗口大小或添加另一个 <article> 元素,布局仍然可以正常工作。

flex:缩写与长写

flex 是一个简写属性,可以指定多达三个不同的值:

  • 我们上面讨论的无单位比例值。这可以使用 flex-grow 长写属性单独指定。
  • 第二个无单位比例值,flex-shrink,当 flex 项目溢出其容器时生效。此值指定项目将收缩多少以防止溢出。这是一个相当高级的 flexbox 功能,我们不会在本文中进一步讨论。
  • 我们上面讨论的最小尺寸值。这可以使用 flex-basis 长写值单独指定。

我们建议您除非真正必要(例如,覆盖之前设置的内容),否则不要使用长写 flex 属性。它们会导致编写大量额外代码,并且可能会有些混乱。

水平和垂直对齐

您还可以使用 flexbox 功能沿主轴或交叉轴对齐 flex 项目。让我们通过查看一个新示例来探索这一点。

html
<div>
  <button>Smile</button>
  <button>Laugh</button>
  <button>Wink</button>
  <button>Shrug</button>
  <button>Blush</button>
</div>
css
body {
  font-family: sans-serif;
  width: 90%;
  max-width: 960px;
  margin: 10px auto;
}
div {
  height: 100px;
  border: 1px solid black;
}
button {
  font-size: 18px;
  line-height: 1.5;
  width: 15%;
}
/* Add your flexbox CSS below here */

我们将把它变成一个整洁灵活的按钮/工具栏。目前,您会看到一个水平菜单栏,其中一些按钮挤在左上角。

首先,获取此示例的本地副本。

现在,将以下内容添加到示例 CSS 的底部:

css
div {
  display: flex;
  align-items: center;
  justify-content: space-around;
}

刷新页面,您会看到按钮现在在水平和垂直方向上都很好地居中。我们通过两个新属性实现了这一点。通过将 align-items 属性设置为 center,flex 项目位于交叉轴的中心。通过将 justify-content 属性设置为 space-around,flex 项目沿主轴均匀分布。

align-items 属性控制 flex 项目在交叉轴上的位置。

  • 默认情况下,值为 normal,在 flexbox 中其行为类似于 stretch。这将所有 flex 项目拉伸以填充父元素在交叉轴方向上的空间。如果父元素在交叉轴方向上没有固定大小,那么所有 flex 项目将变得与最高(或最宽)的 flex 项目一样高(或宽)。这就是为什么我们第一个示例的列默认高度相等的原因。
  • 我们在上面的代码中使用的 center 值导致项目保持其固有尺寸,但在交叉轴上居中。这就是我们当前示例中的按钮垂直居中的原因。
  • 你也可以使用 flex-startself-startstartflex-endself-endend 等值,它们将分别将所有项目对齐到交叉轴的起始和结束位置。baseline 值将使 flex 项目按其基线对齐;基本上,每个 flex 项目第一行文本的底部将与交叉起始点与该基线之间距离最大的元素第一行底部对齐。有关详细信息,请参阅 align-items

你可以通过对单个 flex 项目应用 align-self 属性来覆盖 align-items 的行为。例如,尝试将以下内容添加到你的 CSS 中:

css
button:first-child {
  align-self: flex-end;
}

看看这有什么影响,完成后再将其删除。

justify-content 控制 flex 项目在主轴上的位置。

  • 默认值为 normal,其行为等同于 start,这使得所有项目都位于主轴的起始位置。
  • 你可以使用 endflex-end 使它们位于末尾。
  • leftright 值根据书写模式方向的行为与 startend 相同。
  • center 也是 justify-content 的一个值。它将使 flex 项目位于主轴的中心。
  • 我们上面使用的值 space-around 很有用——它将所有项目沿主轴均匀分布,两端留有一些空间。
  • 还有一个值,space-between,它与 space-around 非常相似,只是它两端不留任何空间。

justify-items 属性在 flexbox 布局中被忽略。

我们鼓励您在继续之前尝试使用这些值,看看它们是如何工作的。

排序弹性项

Flexbox 还有一个功能,可以改变 flex 项目的布局顺序而不影响源顺序。这是传统布局方法无法实现的另一个功能。

尝试将以下 CSS 添加到您的按钮栏示例代码中:

css
button:first-child {
  order: 1;
}

刷新后,您会看到“Smile”按钮已移到主轴的末尾。让我们更详细地讨论一下它是如何工作的。

  • 默认情况下,所有 flex 项目的 order 值为 0
  • 具有较高指定 order 值的 flex 项目将在显示顺序中出现在具有较低 order 值的项目之后。
  • 具有相同顺序值的 flex 项目将按其源顺序显示。因此,如果您有四个项目,其顺序值分别设置为 2110,则它们的显示顺序将是第 4 个、第 2 个、第 3 个,然后是第 1 个。
  • 第三个项目出现在第二个项目之后,因为它具有相同的顺序值并且在源顺序中位于其之后。

您可以设置负数 order 值,使项目出现在值为 0 的项目之前。例如,您可以使用以下规则使“Blush”按钮出现在主轴的起始位置:

css
button:last-child {
  order: -1;
}

虽然可以使用 order 改变顺序,但 Tab 键的焦点顺序保持与代码顺序相同。改变可聚焦元素的顺序可能会对使用键盘的用户造成负面影响!

嵌套弹性盒

使用 flexbox 可以创建一些相当复杂的布局。将 flex 项目也设置为 flex 容器是完全可以的,这样其子元素也像弹性盒一样布局。

这个复杂的布局中有几个 flex 项目也是 flex 容器。它的 HTML 相当简单。我们有一个 <section> 元素,其中包含三个 <article>。第三个 <article> 包含三个 <div>,第一个 <div> 包含五个 <button>

section - article
          article
          article - div - button
                    div   button
                    div   button
                          button
                          button

让我们看看我们用于布局的代码。

首先,我们将 <section> 的子元素设置为弹性盒布局。

css
section {
  display: flex;
}

接下来,我们在 <article> 元素本身上设置了一些 flex 值。特别注意这里的第二条规则:我们正在设置第三个 <article> 的子元素也像 flex 项目一样布局,但这次我们将其布局为列。

css
article {
  flex: 1 100px;
}

article:nth-of-type(3) {
  flex: 3 100px;
  display: flex;
  flex-flow: column;
}

接下来,我们选择第一个 <div>。我们首先使用 flex: 1 100px; 有效地赋予它 100px 的最小高度,然后我们将其子元素(<button> 元素)也设置为像 flex 项目一样布局。在这里,我们将它们布局为可换行的行,并像我们之前看到的单个按钮示例一样,将它们对齐在可用空间的中心。

css
article:nth-of-type(3) div:first-child {
  flex: 1 100px;
  display: flex;
  flex-flow: row wrap;
  align-items: center;
  justify-content: space-around;
}

最后,我们设置按钮的一些大小。这次通过给它一个 1 auto 的 flex 值。这会产生一个非常有趣的效果,如果您尝试调整浏览器窗口宽度,您会看到这一点。按钮将占据尽可能多的空间。一行中会舒适地容纳尽可能多的按钮;超出此范围,它们将换到新行。

css
button {
  flex: 1 auto;
  margin: 5px;
  font-size: 18px;
  line-height: 1.5;
}

总结

至此,我们对 flexbox 基础知识的介绍就结束了。我们希望您玩得开心,并在进一步学习时好好尝试。在下一篇文章中,我们将为您提供一些测试,您可以使用它们来检查您对所有这些信息的理解和掌握程度。

另见