Flexbox

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

先决条件 HTML 基础知识(学习 HTML 简介),以及 CSS 工作原理的概念(学习 CSS 简介)。
目标 学习如何使用 flexbox 布局系统创建网页布局。

为什么选择 flexbox?

CSS 弹性盒子布局使您可以

  • 垂直居中其父元素内的内容块。
  • 使容器的所有子元素占用可用宽度/高度的相等部分,而不管可用宽度/高度是多少。
  • 即使多列布局中的列包含不同数量的内容,也使所有列都采用相同的高度。

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

介绍一个简单的示例

在本文中,您将完成一系列练习,以帮助您了解 flexbox 的工作原理。首先,您应该在本地复制第一个入门文件 — flexbox0.html(来自我们的 GitHub 仓库)。在现代浏览器(如 Firefox 或 Chrome)中加载它,并在代码编辑器中查看代码。您也可以 在这里查看在线版本

Image showing the starting point of flexbox tutorial

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

指定哪些元素作为弹性盒子进行布局

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

css
section {
  display: flex;
}

这会导致 <section> 元素成为Flex 容器,其子元素成为Flex 项目。如下所示

A two row container that includes a single column in the first row and a 3-column layout in the second row that shows how a webpage can be divided into different layouts depending on the contents

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

让我们回顾一下这里发生了什么。在元素上添加 displayflex 会将其转换为 Flex 容器。就其与页面其余部分的交互方式而言,容器显示为 块级内容。当元素转换为 Flex 容器时,其子元素会转换为(并作为)Flex 项目进行布局。

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

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

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.

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

在阅读后续部分时,请记住这些术语。如果您对任何用到的术语感到困惑,可以随时参考它。

列还是行?

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

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

css
flex-direction: column;

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

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

换行

在您的布局中具有固定宽度或高度时,会产生一个问题,即最终您的弹性盒子子元素会溢出其容器,从而破坏布局。查看我们的flexbox-wrap0.html示例,并尝试在线查看(如果您想按照此示例进行操作,请立即复制此文件的本地副本)

The Sample flexbox example has all the flex items laid out in a single row of the flex container. The eighth flex item overflows the browser window, and the page has visible horizontal and vertical scroll bars as it cannot be accommodated within the width of the window as the previous seven flex items have taken the space available within the viewport.

在这里,我们看到子元素确实溢出了其容器。默认情况下,如果flex-direction设置为row,浏览器会尝试将所有弹性项目放置在同一行中;如果flex-direction设置为column,则会放置在同一列中。解决此问题的一种方法是将以下声明添加到您的<section>规则中

css
flex-wrap: wrap;

此外,将以下声明添加到您的<article>规则中

css
flex: 200px;

现在试试看。您会看到包含此声明后布局看起来好多了。

Flex items are laid out in multiple rows in the flex container. The flex-wrap property is set to 'wrap' in the flex container which displays the flex items in a new row if the flex items in the previous row overflow outside the flexbox container. Each flex item is given a width of 200 pixels. All the items are stretched to be the same height, as tall as the flex item with the most content.

现在我们有多行。每行包含尽可能多的弹性盒子子元素。任何溢出都会向下移动到下一行。flex: 200px声明设置在文章上,这意味着每个文章的宽度至少为200px。我们稍后将更详细地讨论此属性。您可能还会注意到,最后一行上的最后几个子元素都变宽了,以便仍然填满整行。

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

flex-flow 简写

在这一点上,值得注意的是,flex-directionflex-wrap存在一个简写形式:flex-flow。例如,您可以替换

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

css
flex-flow: row wrap;

Flex 项目的灵活大小调整

现在让我们回到我们的第一个示例,看看如何控制弹性项目相对于其他弹性项目占据的空间比例。打开您本地复制的flexbox0.html,或将flexbox1.html作为新的起点复制一份(在线查看)。

首先,将以下规则添加到 CSS 的底部

css
article {
  flex: 1;
}

这是一个无单位的比例值,它决定了每个弹性项目沿主轴占据的空间相对于其他弹性项目的多少。在本例中,我们为每个<article>元素提供了相同的值(值为1),这意味着它们都将占用填充和边距等属性设置后剩余的相同数量的空闲空间。此值在弹性项目之间按比例共享:为每个弹性项目提供值为400000的值将产生完全相同的效果。

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

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

现在刷新页面,您会看到第三个<article>占据的可用宽度是其他两个的两倍。现在总共有四个比例单位可用(因为 1 + 1 + 2 = 4)。前两个弹性项目各有 1 个单位,因此它们分别占用可用空间的 1/4。第三个有 2 个单位,因此它占据了可用空间的 2/4(或一半)。

您还可以指定 flex 值内的最小尺寸值。尝试像这样更新您现有的文章规则

css
article {
  flex: 1 200px;
}

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

这基本上表示:“每个弹性项目首先将获得可用空间的200px。之后,剩余的可用空间将根据比例单位共享。”尝试刷新,您会看到空间共享方式的不同。

A flex container with three flex items. The third flex item is slightly larger than the first two.

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

Flexbox 的真正价值体现在其灵活性和响应性上。如果您调整浏览器窗口大小或添加另一个<article>元素,布局将继续正常工作。

flex:简写与完整写法

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

  • 我们上面讨论过的无单位比例值。这可以使用flex-grow完整属性单独指定。
  • 第二个无单位比例值flex-shrink,当弹性项目溢出其容器时,它就会发挥作用。此值指定为了防止溢出,项目将缩小多少。这是一个相当高级的 Flexbox 功能,我们不会在本文章中进一步介绍它。
  • 我们上面讨论过的最小尺寸值。这可以使用flex-basis完整值单独指定。

我们建议您不要使用完整形式的 flex 属性,除非您确实需要这样做(例如,覆盖之前设置的内容)。它们会导致编写大量额外的代码,并且可能有点令人困惑。

水平和垂直对齐

您还可以使用 Flexbox 功能沿主轴或交叉轴对齐弹性项目。让我们通过查看一个新示例来探索这一点:flex-align0.html在线查看)。我们将把它变成一个简洁、灵活的按钮/工具栏。目前,您会看到一个水平菜单栏,其中一些按钮挤在左上角。

Five buttons are laid out in a row in a flex container. The buttons are jammed into the top left-hand corner that doesn't look neat.

首先,复制此示例的本地副本。

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

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

Five buttons are laid out in a row in a flex container. The flex items are positioned vertically centered and they are evenly spaced out horizontally.

刷新页面,您会看到按钮现在水平和垂直居中。我们通过两个新属性完成了此操作。通过将align-items属性设置为center,弹性项目位于交叉轴的中心。通过将justify-content属性设置为space-around,弹性项目沿主轴均匀分布。

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

  • 默认情况下,值为normal,在 Flexbox 中的行为与stretch相同。这会将所有弹性项目拉伸以填充父元素在交叉轴方向上的区域。如果父元素在交叉轴方向上没有固定大小,则所有弹性项目的高度(或宽度)都将与最高的(或最宽的)弹性项目相同。这就是我们的第一个示例默认情况下具有等高列的原因。
  • 我们在上面代码中使用的center值会导致项目保持其固有尺寸,但在交叉轴上居中。这就是我们当前示例的按钮垂直居中的原因。
  • 您还可以使用诸如flex-startself-startstart以及flex-endself-endend之类的值,它们将分别在交叉轴的开头和结尾处对齐所有项目。baseline值将根据其基线对齐弹性项目;基本上,每个弹性项目第一行文本的底部将与具有最大距离(从交叉起点到该基线)的元素的第一行底部对齐。有关完整详细信息,请参阅align-items

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

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

Five buttons are laid out in a row in a flex container. All the flex items except the first one are positioned at the center of the cross-axis, or vertically centered. The first item is flush against the bottom of the flex container, at the end of the cross-axis The flex items are spaced evenly along the main-axis, or width, of the container.

看看这会产生什么影响,并在完成后将其删除。

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

  • 默认值为normal,其行为与start相同,这使得所有项目都位于主轴的开头。
  • 您可以使用endflex-end使其位于末尾。
  • leftright值的行为与startend相同,具体取决于书写模式方向。
  • center也是justify-content的值。它将使弹性项目位于主轴的中心。
  • 我们在上面使用的值space-around非常有用——它将所有项目均匀地分布在主轴上,并在两端留出一些空间。
  • 还有另一个值space-between,它与space-around非常相似,只是它在两端不留任何空间。

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

我们鼓励您在继续之前尝试这些值以了解它们是如何工作的。

Flex 项目排序

Flexbox 还具有一个功能,可以更改弹性项目的布局顺序,而不会影响源顺序。这是传统布局方法无法实现的另一件事。

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

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

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

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

您可以设置负顺序值,使项目出现在值为0的项目之前。例如,您可以使用以下规则使“Blush”按钮出现在主轴的开头

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

虽然您可以使用order更改顺序,但选项卡顺序与代码顺序相同。更改可聚焦元素的顺序会对键盘用户的可用性产生负面影响!

嵌套 Flex 盒子

可以使用 Flexbox 创建一些非常复杂的布局。将弹性项目也设置为弹性容器完全没问题,以便其子元素也像弹性盒子一样布局。查看complex-flexbox.html在线查看)。

The Sample flexbox example has three flex item children laid out in a row. The first two are the same width, the third is slightly wider. The third flex item is also a flex container. It has a set of buttons in two rows followed by text. The first row of buttons has 4 buttons that are laid out in a row; the buttons are the same width, taking up the full width of the container. The second row has a single button that takes up the entire width of the row on its own.

此复杂布局有一些弹性项目,它们也是弹性容器。此 HTML 非常简单。我们有一个包含三个<article><section>元素。第三个<article>包含三个<div>,第一个<div>包含五个<button>

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

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

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

css
section {
  display: flex;
}

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

css
article {
  flex: 1 200px;
}

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

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

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

总结

这结束了我们对 Flexbox 基础知识的介绍。我们希望您玩得开心,并在学习过程中多加练习。接下来,我们将看看 CSS 布局的另一个重要方面:CSS 网格

另请参阅