1. 什么是 Flexbox?
Flexbox 是“Flexible Box Layout”的缩写。它是一个 CSS 布局模型,可以简化复杂布局的创建。它提供了一种灵活的方式来对齐元素并在容器元素内分配空间。
在 Flexbox 出现之前,创建复杂的布局和响应式网页非常艰难。你需要 CSS 浮动和位置属性的组合。这需要许多变通方法和技巧。但使用 Flexbox,你现在可以面对更少的困难,使用更少的代码行数来实现以下操作:
- 使用
justify-content
和align-items
等属性对齐和居中元素。 - 无需编写大量媒体查询即可开发响应式布局。
- 在不改变 HTML 结构的情况下重新排序元素。
- 创建相同高度的列,无需任何额外的 HTML 元素或背景图片。
2. 如何使用 Flexbox?
Flexbox 布局模型是双向的。这意味着你可以按行、列或两者排列元素。
2.1 主轴和交叉轴
关于 Flexbox,你首先需要了解的是轴的概念。每个 Flex 容器( display
属性设置为 flex 或 inline-flex
的元素)都有一个主轴和一个交叉轴。主轴是水平的还是垂直的,具体取决于 flex-direction
的值。
在这个示例中,主轴是水平的,交叉轴是垂直的。flex-direction
的值是 row
。
在这个示例中,主轴是垂直的,交叉轴是水平的。flex-direction
的值是 column
。
2.2 Flex 容器和 Flex 子项
要使用 Flexbox 的所有属性,你需要将元素的 display
属性设置为 flex
或 inline-flex
。这将使元素成为 Flex 容器,并且该元素的子元素变成 Flex 子项。 举个例子:
1 | <section class="container"> |
1 | .container { |
.container
元素现在是一个 Flex 容器。这三个 div
元素是 .container
元素的直接子元素,这使得它们成为 Flex 子项。但第三个 div
内的段落元素不是 Flex 子项。这是因为它不是 .container
元素的直接子元素。
2.3 Flex 和 Inline-flex
使用 flex
和 inline-flex
都可以使元素成为 Flex 容器。不同之处在于它们与周围元素的交互方式。
display: flex
:这使得 Flex 容器的行为类似块级元素。Flex 容器占据其父元素的整个可用宽度。它会在新的一行开始,并且其后的元素也会在新的一行开始。比如:
1 | <button>Button One</button> |
1 | .container { |
如上所示,当你使用 display: flex
时,Flex 容器的行为类似于块元素,.container
元素占据了主体(其父元素)的整个可用宽度。
display: inline-flex
:这使得 Flex 容器的行为类似于内联元素。Flex 容器只占据它的内容宽度。它不会在新的一行开始,而是与周围元素在同一行。如果将上面的例子改为display: inline-flex
,则效果如下所示:
如图所示,Flex 容器不占据其父容器的整个宽度。它仅使用其内容所需的水平空间。
3. Flex 容器属性
Flex 容器属性允许你控制 Flex 容器内 Flex 子项的布局和对齐方式。
⚠️ 注意:以下属性仅适用于 Flex 容器,而不是 Flex 子项。
3.1 flex-direction 属性
flex-direction
属性决定主轴的方向。定义了 Flex 子项的显示方向,它可以取以下值中的任意一个:
row
:默认值。主轴从左到右。row-reverse
:主轴从右到左。column
:主轴从上到下。column-reverse
:主轴从下到上。
让我们看一个例子,看看它是如何工作的:
1 | <div class="names-container"> |
1 | .names-container { |
flex-direction: row
: 这会从左到右水平显示 Flex 子项。
flex-direction: row-reverse
: 这会从右到左水平显示 Flex 子项。
flex-direction: column
: 这会从上到下垂直显示 Flex 子项。
flex-direction: column-reverse
: 这会从下到上垂直显示 Flex 子项。
⚠️ 当你使用
row-reverse
和column-reverse
时,有些事情你需要记住。正如你已经看到的,这两个值会影响屏幕上元素的视觉顺序。但是这些元素在 HTML 中的顺序保持不变,而这些元素在 HTML 中的顺序就是屏幕阅读器和键盘导航控件使用的顺序。
在示例中,当你使用row-reverse
时,你首先在屏幕上看到 Jack 的名字,然后是 Jane、John 和 Jill。但是对于使用屏幕阅读器的人来说,他们将会按照 HTML 中的出现顺序而不是屏幕上的顺序来听到这些名字。在这种情况下,他们将首先听到 Jill 的名字,然后是 John、Jane 和 Jack。
3.2 flex-wrap 属性
有时,Flex 容器内的空间不足以容纳 Flex 子项, 在这种情况下,你可以使用 flex-wrap
属性来选择是让 Flex 子项溢出还是另起一行。
flex-wrap 属性接受以下任何值:
nowrap
(默认值):禁止 Flex 子项换行。wrap
:允许 Flex 子项在新行开始。wrap-reverse
:允许 Flex 子项在新行开始,并且新行的方向与wrap
方向相反。
要看到 flex-wrap
的效果,让我们向 names-container
添加超过四个的名字:
1 | <div class="names-container"> |
1 | .names-container { |
flex-wrap: nowrap
: 这会禁止 Flex 子项换行。在这个例子中,由于空间不足,三个名字溢出容器。
flex-wrap: wrap
: 当空间不足时,这会将 Flex 子项换行或推到下一行。
flex-wrap: wrap-reverse
: 这会允许 Flex 子项在新行开始,并且新行的方向与 wrap
方向相反。
3.3 flex-flow 缩写属性
flex-flow
属性是 flex-direction
和 flex-wrap
属性的缩写。这意味着当你使用 flex-flow
时,只需要一行代码即可同时应用这两个属性。如下所示:
1 | .names-container { |
3.4 justify-content 属性
justify-content
属性处理 Flex 容器主轴上 Flex 子项的对齐方式。你可以使用它来处理主轴上空间的分配方式。该属性可以取以下任何值:
flex-start
(默认值):Flex 子项向主轴起点对齐。flex-end
:Flex 子项向主轴终点对齐。center
:Flex 子项居中对齐。space-between
:Flex 子项平均分布,两端对齐。space-around
:Flex 子项平均分布,每个项两侧留有空白。space-evenly
:Flex 子项平均分布,所有项间均匀分布。
1 | .names-container { |
justify-content: flex-start
:这将把 Flex 子项放置在 flex-direction
的起始位置。如果主轴是水平的(flex-direction
的属性值是 row),它会将 Flex 子项对齐到左侧。如果主轴是垂直的(flex-direction
的属性值是 column),它会将 Flex 子项对齐到顶部。
justify-content: flex-end
:这会将 Flex 子项放置在主轴的 flex-direction 的末尾。
justify-content: center
:这会将 Flex 子项居中对齐。
justify-content: space-between
: 这会将第一个 Flex 子项放置在主轴的起始位置,并将最后一个项放置在主轴的末尾。然后,主轴上的空间在这些元素之间均匀分布。(space-between
并不改变元素本身大小,它的作用只是将元素之间的间距均匀划分了):
justify-content: space-around
: 这也会在 Flex 子项之间均匀分配空间。这里的关键区别在于,第一个项之前和最后一个项之后的空间是 Flex 子项之间空间的一半。
justify-content: space-evenly
: 这会在 Flex 子项之间均匀分配空间。这意味着每项前后的空间都相同。(在 space-between
模式中,首项和末项在没有其他 CSS 设置的前提下,与 Flex 容器的左外边距/右外边距为 0,即在 Flex 容器中与最左端和最右端“顶格”。而space-evenly
则是保证每一个 Flex 子项左侧和右侧的边距均是相等的,即 Flex 子项之间的间距和 Flex 子项与 Flex 容器边界的间距都相等,该模式下 Flex 子项与 Flex 容器的左右边界也存在间距。)
3.5 align-items 属性
align-items
属性处理 Flex 子项在 Flex 容器交叉轴上的对齐方式。它可以采用以下任意值:
stretch
(默认值):默认值。Flex 子项拉伸以填充 Flex 容器。flex-start
:Flex 子项向交叉轴起点对齐。flex-end
:Flex 子项向交叉轴终点对齐。center
:Flex 子项居中对齐。baseline
:Flex 子项与容器基线对齐。
1 | .names-container { |
align-items: stretch
: 这会默认拉伸 Flex 子项以填充 Flex 容器。
align-items: flex-start
: 这会将 Flex 子项放置在交叉轴的起始位置。如果交叉轴是垂直的,它会将 Flex 子项放置在顶部。
align-items: flex-end
: 这会将 Flex 子项放置在交叉轴的末尾。如果交叉轴是垂直的,如下例所示, align-items: 它会将 Flex 子项放置在底部。
align-items: center
: 这会将 Flex 子项在交叉轴上居中对齐。
align-items: baseline
: 当你使用 baseline 值时,Flex 子项目的排列方式是使它们的基线对齐。
3.6 align-content 属性
当你有一个带有换行(或多个 Flex 行)的 Flex 容器时,你可能需要对这些行进行对齐以根据需要分配空间。这时你可以使用 align-content 。
align-content
属性可以取以下任意值:
stretch
(默认值):默认值。Flex 行拉伸以填充 Flex 容器。flex-start
:Flex 行向交叉轴起点对齐。flex-end
:Flex 行向交叉轴终点对齐。center
:Flex 行居中对齐。space-between
:Flex 行平均分布,两端对齐。space-around
:Flex 行平均分布,每个项两侧留有空白。space-evenly
:Flex 行平均分布,所有项间均匀分布。
1 | .names-container { |
在下面的示例中,names-container
中有 11 个名字,并且names-container
的 flex-wrap
值为 wrap。这意味着你可以应用 align-content
属性来改变 Flex 行的对齐方式。
align-content: stretch
:这会拉伸 Flex 行以填充 Flex 容器交叉轴内的空间。
align-content: flex-start
: 这将把 Flex 行放置在容器的交叉轴起始位置。例如,如果交叉轴像 names-container
一样是垂直的,它就会将 Flex 行放置在顶部。
align-content: flex-end
: 这将把 Flex 行放置在容器的交叉轴末尾。
align-content: center
: 这会将 Flex 行放置在容器的交叉轴中心位置。
align-content: space-between
: 这会将第一行放置在交叉轴的起始位置。它还会将最后一行放置在交叉轴的末尾位置。然后,交叉轴上的空间会均匀分配给这些行。
align-content: space-around
: 这也会在 Flex 行之间均匀分配空间。这里的主要区别在于,第一行之前和最后一行之后的空间是 Flex 行之间空间的一半。
align-content: space-evenly
: 这会在 Flex 行之间均匀分配空间。这意味着每行前后的空间都相同。
3.7 place-content 属性
如果你需要同时使用 justify-content
和 align-content
属性,你可以使用 place-content
缩写属性。它可以接受一个或两个值。当你给它一个单一值时,浏览器会为 justify-content
和 align-content
应用相同的值。当你为 place-content
提供两个值时,第一个值将用于 align-content
,第二个值将用于 justify-content
。让我们看一个示例:
1 | .names-container { |
4. Flex 子项属性
Flex 容器的每个直接子元素都是 Flex 子项。Flexbox 还有一些属性,你可以应用到单个 Flex 子项上。它们包括以下内容:
order
属性:你可以使用order
属性来控制 Flex 子项的显示顺序。默认情况下,Flex 子项按照它们在 HTML 中的顺序来显示。align-self
属性:你可以使用align-self
属性来控制单个 Flex 子项的对齐方式。默认情况下,align-items
属性的值会应用到 Flex 子项上。flex-grow
属性:你可以使用flex-grow
属性来控制 Flex 子项的扩展比例。默认情况下,Flex 子项的扩展比例为 0,即它们不会扩展。flex-shrink
属性:你可以使用flex-shrink
属性来控制 Flex 子项的收缩比例。默认情况下,Flex 子项的收缩比例为 1,即它们会收缩以适应 Flex 容器。flex-basis
属性:你可以使用flex-basis
属性来设置 Flex 子项的初始大小。默认情况下,Flex 子项的初始大小为 auto,即它们会根据内容的大小来确定大小。flex
属性:你可以使用flex
属性来一次性设置flex-grow
、flex-shrink
和flex-basis
属性。
4.1 order 属性
order 属性确定 Flex 子项的出现顺序。给这个属性的赋值必须是一个数字。一个数字较小的 Flex 子项将出现在数字较大的 Flex 子项之前。
在 HTML 代码中,四个名字的顺序如下:
1 | <div class="names-container"> |
你可以使用 order 属性改变屏幕上的出现顺序。以下是没有 order 属性时它们的显示方式:
当你添加以下 order 属性时,它们的显示方式如下所示:
1 | .names-container { |
‼️ 尽管屏幕上的出现顺序改变了,但 HTML 中的顺序保持不变。而屏幕阅读器使用的是 HTML 中的顺序。如果可能,最佳实践是在 HTML 中改变顺序,而不是使用 Flexbox 进行更改。
4.2 align-self 属性
你可以使用 align-self
属性为一个 Flex 子项单独设置不同的对齐方式。它的工作方式与 align-items
属性相同。不同之处在于, align-items
适用于所有 Flex 子项,而 align-self
属性仅适用于特定项。 例如:
1 | .names-container { |
在这个示例中,names-container
的 align-items
属性设置为 center
,这会使所有名字都居中对齐。但是使用 align-self
属性,你可以用 flex-start
将 Jill 的名字卡片对齐到顶部。
4.3 flex-grow 属性
当你将容器的 display 设置为 flex 时,通常会在排列项之后有一些额外的空间。请参阅下面的示例:
1 | .names-container { |
浏览器会将多余的空间视为 1 的值。这意味着当你为其中一个 Flex 子项的 flex-grow
设置为 0.5 时,浏览器会将剩余空间的一半添加到该项的大小中。
1 | #jill { |
在这个示例中,Jill 的 flex-grow
属性设置为 0.5,这会使浏览器将剩余空间的一半添加到 Jill 的大小中。flex-grow
属性使 Jill 的大小大于其初始大小。
如果你只给一个 Flex 子项设置了 flex-grow
值为 1 ,浏览器将把所有额外的空间都分配给该项。下面两个代码片段对 Jill 的卡片具有相同的效果:
1 | #jill { |
当你为多个元素添加 flex-grow
值会发生什么?浏览器将按比例为它们共享额外的空间。
例如,当你给 Jane 设置 flex-grow
为 3,给 Jack 设置 flex-grow 为 1 时,浏览器将以 3:1 的比例分享额外的空间。这意味着额外空间的总值变为 4(3+1)。Jane 将获得额外空间的 3/4,而 Jack 将获得 1/4, 如下图所示:
4.4 flex-shrink 属性
flex-shrink
属性与 flex-grow
相反。当你希望在存在额外空间时增加 Flex 子项的大小时,你可以使用 flex-grow
。但是,当 Flex 容器中的空间不足时,你可以使用 flex-shrink
来减小 Flex 子项的大小。
1 | <div class="numbers-container"> |
1 | .numbers-container { |
在这个示例中,四个数字每个的宽度都设置为 150px(总共 600px)。但 numbers-container
的宽度只有 400px,这是不够的。卡片必须缩小以适应可用空间。数字 1 的 flex-shrink
的值为 2, 其他子项没有设置(默认值是 1),这意味着数字 1 在缩小时会以两倍于默认或其他设置较低的元素的比例进行收缩。
如果你不希望一个 Flex 子项缩小怎么办?为了防止 Flex 子项收缩,给它一个 flex-shrink
值为 0。例如,当你为 Number 1 指定 flex-shrink
为 0 时,它将保持 150px 的宽度。其他 Flex 子项将缩小以适应剩余空间。
4.5 flex-basis 属性
你可以使用 flex-basis
属性来设置特定 Flex 子项的默认长度。这取决于 flex-direction
,可以是宽度或高度。
- 如果
flex-direction
是row
或row-reverse
,则flex-basis
的值就成为 Flex 子项的初始宽度。 - 如果
flex-direction
是column
或column-reverse
,那么flex-basis
的值就成为 Flex 子项的初始高度。
例如:
1 | .names-container { |
在这个示例中,div 的高度设置为 20px。但 Jane 得到的 flex-basis 值为 60px。这意味着 Jane 的初始高度为 60px,而不是 20px。
下面是另一个示例。这次,flex-direction
是 row。这意味着 flex-basis 将设置 Flex 子项的宽度。
1 | .names-container { |
在这个示例中,div 的宽度设置为 70px。但 Jane 得到的 flex-basis 值为 140px。这意味着 Jane 的初始宽度为 140px,而不是 70px。
4.6 flex 缩写属性
你可以使用 flex 作为 flex-grow
、flex-shrink
和 flex-basis
属性的缩写。
1 | .flex-item { |
在这个示例中,flex-grow
的值为 2,flex-shrink
的值为 0,flex-basis
的值为 50px。这等价于 flex: 2 0 50px;
。
值的顺序很重要。浏览器将第一个值分配给 flex-grow
,第二个值分配给 flex-shrink
,第三个值分配给 flex-basis
。这意味着如果你给 flex 一个值为 2,浏览器将使用 2 作为 flex-grow
。然后它将 flex-shrink
设置为 0,将 flex-basis
设置为 auto。
💡 flex 的默认值是 1 0 auto。
5. 一些 Flexbox 常见用例
5.1 元素居中
让许多前端开发人员头疼的问题之一是元素居中。 Flexbox 对此提供了一个完美的解决方案。
涉及两个步骤:
- 通过将
display
设置为flex
使父元素成为 Flex 容器。 - 将
justify-content
和align-items
都设置为center
。
1 | <div class="name-container"> |
1 | .name-container { |
无论你是试着居中文本、图像,还是整个导航栏,这都能完美运行。
5.2 全屏布局
经典的全屏布局由顶部、底部和主体三部分组成,其特性为三部分左右满屏拉伸、顶部底部高度固定和主体高度自适应。该布局很常见,也是很多 Web 应用主体的主流布局。通常使用<header>、<footer>和<main>
三个标签语义化排版,<main>
内还可加入<aside>
侧栏或其他语义化标签。
使用 flex 实现会非常简洁:display:flex
默认让子节点横向排列,需声明flex-direction:column
改变子节点排列方向为纵向排列;顶部与底部高度固定,主体声明 flex:1
自适应高度。
1 | <div class="fullscreen-layout"> |
1 | .fullscreen-layout { |
⚠️ 若 <main>
需表现为可滚动状态,千万不要声明 overflow:auto
让容器自适应滚动,这样做有可能因为其他格式化上下文的影响而导致自适应滚动失效或产生其他未知效果。解决方案是在 <main>
中插入一个 <div>
并声明如下。
1 | .fullscreen-layout { |
5.3 两列布局
经典的两列布局由左右两列组成,其特性为一列宽度固定、另一列宽度自适应和两列高度固定且相等。以下以左列宽度固定与右列宽度自适应为例,反之同理。
使用 flex 实现非常简洁。左列声明固定宽度,右列声明 flex:1
自适应宽度。
1 | <div class="two-column-layout"> |
1 | .two-column-layout { |
5.4 三列布局
经典的三列布局由左中右三列组成,其特性为连续两列宽度固定、剩余一列宽度自适应和三列高度固定且相等。以下以左中列宽度固定与右列宽度自适应为例,反之同理。整体的实现原理与上述两列布局一样。
1 | <div class="three-column-layout"> |
1 | .three-column-layout { |
5.5 圣杯布局/双飞翼布局
经典的圣杯布局与双飞翼布局都是由左中右三列组成,其特性为左右两列宽度固定、中间一列宽度自适应和三列高度固定且相等。其实也是上述两列布局与三列布局的变体,整体的实现原理与上述 N 列布局一样,可能就是一些细节需注意。
圣杯布局与双飞翼布局大体相同但也存在一点不同,区别在于双飞翼布局中列需加入一个子节点。在常规实现方式中也是在中列中做文章:如何使中列内容不被左右列遮挡。
1 | <div class="grail-layout"> |
1 | .grail-layout { |
6. Flexbox 间隙
你可以使用 gap 属性来调整 Flex 子项之间的间距。gap 可以取两个值:第一个值用于行之间的间距,第二个值用于列之间的间距。
注意:gap 属性应用于 Flex 容器,而不是 Flex 子项。
1 | .names-container { |
如果你想要的行和列之间的间距相同,你可以使用单个值。浏览器将为行和列都应用相同的值。例如:
1 | .names-container { |
如果你需要仅在行之间应用特定的间隙值,你还可以使用属性 row-gap
。如果你需要仅在列之间添加间隙,则使用 column-gap
。例如:仅在行之间添加间隙:
1 | .names-container { |
仅在列之间添加间隙:
1 | .names-container { |