Flexbox
CSS 提供了许多工具和属性,可用于在网页上定位元素。Codecademy 的课程**「盒模型(box model)」和「CSS display」**介绍了一些基本的布局技术。
在本课程中,你将学习弹性盒布局(Flexible Box Layout,简称 flexbox) ,这是一种可以极大简化元素定位的工具。
Flexbox 布局的两个重要组成部分:
- Flex 容器(flex container): 页面上的某个元素,它包含flex 项目(flex items) 。
- Flex 项目(flex items): Flex 容器的所有直接子元素。
重要区别:本课程中介绍的某些属性适用于flex 容器 ,而其他属性适用于flex 项目,这一点需要特别注意!
如何定义一个 Flex 容器?
只需将某个元素的 display 属性设置为 flex 或 inline-flex,该元素就会成为 Flex 容器 。
一旦一个元素成为 Flex 容器,就可以使用多个属性来控制其子元素的行为。
本课程将介绍以下 Flexbox 属性:
justify-content
align-items
flex-grow
flex-shrink
flex-basis
flex
flex-wrap
align-content
flex-direction
flex-flow
Flexbox 是一个强大且优雅的工具,它可以轻松解决许多以往难以处理的布局问题。
display: flex
Flex 容器是创建响应式网页 的有力工具,它可以根据屏幕大小的变化自动调整布局。Flex 容器的子元素(flex items)会根据其父容器的大小和位置进行调整,从而实现灵活的布局。
任何元素都可以成为 Flex 容器
如何定义一个 Flex 容器?
要让某个元素成为 Flex 容器 ,需要将它的 display 属性设置为 flex:
css
div.container {
display: flex;
}
在上面的示例中,所有 class="container" 的 div 元素都是 Flex 容器 。
如果它们包含子元素,这些子元素就会自动成为 Flex 项目(flex items) 。
Flex 容器的影响
- 具有
display: flex;的div仍然是块级元素(block-level) ,不会与其他元素出现在同一行。 - 但是,它的子元素不会自动换行 ,而是会按照
flex规则排列。
在接下来的练习中,我们将详细讲解 flex 布局如何影响子元素的排列方式
inline-flex
在上一个练习中,我们观察到:当我们给 div(一个块级元素)设置 display: flex; 时,它仍然是块级元素 。
但如果我们希望多个 Flex 容器在同一行内显示,该怎么办?
通常,我们可以使用 display: inline; 让 div 变为行内元素。但在 Flexbox 中,我们可以使用 display: inline-flex; ,它允许我们创建既是 Flex 容器,又是行内元素的布局。
示例 1:两个 inline-flex 容器
html
<div class='container'>
<p>我是 Flex 容器中的文本!</p>
<p>Flex 容器的子元素是 Flex 项目!</p>
</div>
<div class='container'>
<p>我也是 Flex 项目!</p>
<p>我也是!</p>
</div>
css
.container {
width: 200px;
height: 200px;
display: inline-flex;
}
-
这里有两个
div.container,默认情况下,它们会占满整行(块级元素的默认行为)。 -
但是,当
display: inline-flex;时,它们会在页面足够宽的情况下并排显示,而不是像块级元素那样换行。 -
p元素仍然是块级元素,因此在container内是垂直排列的。
示例 2:子元素超出父容器
html
<div class='container'>
<div class='child'>
<h1>1</h1>
</div>
<div class='child'>
<h1>2</h1>
</div>
</div>
css
.container {
width: 200px;
}
.child {
display: inline-flex;
width: 150px;
height: auto;
}
解析:
child容器的总宽度 = 150px + 150px = 300px,超出了container容器的 200px。- 但
inline-flex默认会让子元素缩小,以适应父容器的大小。 - 这个行为与
flex-shrink相关,后面的课程会进一步讲解如何控制子元素的缩放。
justify-content
在之前的练习中,当我们将父容器的 display 值设置为 flex 或 inline-flex 时,所有子元素(即 flex 项目)默认都会移动到父容器的左上角。这是 flex 容器及其子元素的默认行为。
我们可以指定 flex 子元素在主轴(从左到右)的排列方式。关于轴的更多内容,我们将在后续练习中学习。
为了控制子元素从左到右的排列方式 ,我们可以使用 justify-content 属性。
css
.container {
display: flex;
justify-content: flex-end;
}
在上面的示例中,我们将 justify-content 设置为 flex-end,这会使所有 flex 子元素向右对齐。
justify-content 的 5 个常用值:
-
flex-start------ 所有子元素从左侧开始依次排列,元素之间不会有额外的间距。 -
flex-end------ 所有子元素从右侧开始依次排列,元素之间不会有额外的间距。 -
center------ 所有子元素居中对齐,没有额外的间距。 -
space-around------ 所有子元素之间的间距相等,并且两端的间距是中间间距的一半,导致元素之间的间距比两侧的间距大一倍。 -
space-between------ 所有子元素的间距相等 ,但两端不会有额外的间距,即第一个元素紧贴左边,最后一个元素紧贴右边。
额外说明:
-
"没有额外的间距" 意味着
margin和border仍然有效,但不会在元素之间添加额外的空间。 -
justify-content只影响子元素的排列方式,不会改变每个子元素的大小。
align-items
在上一个练习中,你学习了如何使用 justify-content 在 水平方向(主轴) 上排列 flex 容器中的子元素。同样的,我们也可以使用 align-items 在垂直方向(交叉轴) 上对齐 flex 子元素。
align-items 属性用于垂直方向上调整 flex 子元素的排列方式。
css
.container {
align-items: baseline;
}
在上面的示例中,align-items 被设置为 baseline,这意味着所有子元素的文本基线(baseline) 会对齐。
align-items 的 5 个常用值:
-
flex-start------ 所有元素对齐到父容器顶部。 -
flex-end------ 所有元素对齐到父容器底部。 -
center------ 所有元素的中心会对齐到父容器的垂直中间位置。 -
baseline------ 所有元素的文本基线(baseline) 会对齐。适用场景 :当子元素有不同的字体大小、行高或内容高度时,可以使用baseline让它们的文本对齐,而不是顶部或底部对齐。 -
stretch------ 默认值 ,元素会拉伸 ,填充整个父容器的高度(如果子元素有固定height,则不会被拉伸;如果没有设置height或者有min-height,则会拉伸)。
说明:
-
这些值决定了子元素在 交叉轴(cross axis,默认是垂直方向) 上的排列方式。
-
在 flex 布局中,默认的交叉轴是从上到下的方向。
-
你可能对
min-height和max-height不太熟悉,但你应该用过height和width。min-height、max-height、min-width和max-width这些属性可以确保元素的大小不会小于最小值,也不会超过最大值。
flex-grow
在 练习 3 中,我们了解到当 flex 容器 太小时,所有 flex 项目(flex items) 会按比例缩小。然而,如果父容器的空间大于必要的大小,默认情况下 flex 项目不会自动伸展。
flex-grow 属性 允许我们指定 flex 项目是否应该增长以填充容器 ,并且可以控制 哪些项目应该按比例增长得更多或更少。
html
<div class='container'>
<div class='side'>
<h1>我是 flex 容器的一侧!</h1>
</div>
<div class='center'>
<h1>我是 flex 容器的中心!</h1>
</div>
<div class='side'>
<h1>我是 flex 容器的另一侧!</h1>
</div>
</div>
css
.container {
display: flex;
}
.side {
width: 100px;
flex-grow: 1;
}
.center {
width: 100px;
flex-grow: 2;
}
在上面的示例中:
-
.container被设置为display: flex;,因此它的三个子元素(.side和.center)会排列在同一行。 -
如果
.container的宽度超过 300px (默认子元素的总宽度:100px + 100px + 100px),那么 多余的空间将会按照flex-grow进行分配:.side的flex-grow: 1;,表示它们会平均分配部分额外空间。.center的flex-grow: 2;,表示它会比.side多吸收一倍的空间。
例如,如果 .container 额外多出 60px 的空间:
-
.center会吸收 30px(2 份)。 -
每个
.side各吸收 15px(1 份)。
额外说明
-
如果某个元素设置了
max-width,即使它有flex-grow也不会超过最大宽度,即它不会继续增长超出max-width限制。 -
之前学习的
align-items、justify-content等属性是作用在父级 flex 容器 上的,而flex-grow是我们学习的第一个作用在 flex 子元素上的属性。
flex-shrink
就像 flex-grow 属性可以按比例扩展 flex 项目 一样,flex-shrink 属性可以用于指定哪些元素会收缩以及它们收缩的比例。
flex-shrink 介绍
- 当 flex 容器的空间不足以容纳所有 flex 项目时,flex 项目会按比例缩小。
- 默认情况下 ,即使没有显式声明
flex-shrink,flex 项目也会缩小,因为flex-shrink的默认值是1。 - 但 flex 项目 不会自动增长 ,除非明确设置了
flex-grow,因为flex-grow的默认值是0。
html
<div class='container'>
<div class='side'>
<h1>我是 flex 容器的一侧!</h1>
</div>
<div class='center'>
<h1>我是 flex 容器的中心!</h1>
</div>
<div class='side'>
<h1>我是 flex 容器的另一侧!</h1>
</div>
</div>
css
.container {
display: flex;
}
.side {
width: 100px;
flex-shrink: 1;
}
.center {
width: 100px;
flex-shrink: 2;
}
flex-shrink: 1;表示.side会按默认比例缩小。flex-shrink: 2;表示.center会缩小的比例是.side的两倍。
例如,如果 .container 的内容总宽度超出了 60px:
-
.center会缩小 30px(2 份)。 -
每个
.side各缩小 15px(1 份)。
额外说明
flex-grow和flex-shrink都不会影响margin,也就是说,即使元素缩小或增长,它的外边距(margin)仍然保持不变。min-width和max-width的优先级高于flex-shrink和flex-grow,如果元素的min-width设定了最小宽度,即使flex-shrink设定了较高的收缩比例,该元素也不会小于min-width设定的值。flex-shrink只有在 父容器空间不足时才会生效 ,也就是说,当页面窗口变小或者 flex 容器的宽度减少时,才会触发flex-shrink计算。
例子:
css
.top.side {
flex-shrink: 2;
}
效果:
可以看到两边的收缩程度是中间的二倍
css
.middle.side {
flex-shrink: 0;
}
可以看到两边不收缩
css
.bottom.center {
flex-shrink: 2;
}
可以看到中间的收缩程度是两边的二倍
flex-basis
在前两个练习中,div 的尺寸是通过 CSS 设置的高度和宽度来决定的。另一种指定 div 宽度的方式是使用 flex-basis 属性。flex-basis 允许我们在弹性项目(flex item)伸展或收缩之前,先指定它的默认大小。
html
<div class='container'>
<div class='side'>
<h1>Left side!</h1>
</div>
<div class='center'>
<h1>Center!</h1>
</div>
<div class='side'>
<h1>Right side!</h1>
</div>
</div>
css
.container {
display: flex;
}
.side {
flex-grow: 1;
flex-basis: 100px;
}
.center {
flex-grow: 2;
flex-basis: 150px;
}
在上面的示例中,如果 .container 容器的宽度刚好合适(350 像素,再加上一些 外边距(margin) 和 边框(border) 的额外空间),那么 .side 这个类的 div 宽度会是 100 像素,而 .center 这个类的 div 宽度会是 150 像素。
如果 .container 容器的宽度更大,.center 这个 div 会比 .side 这个 div 吸收更多的额外空间,其比例为 2:1。
如果我们还给这些 div 指定了 flex-shrink 的值,那么相同的规则也适用于元素缩小时的情况。
-
flex-basis用于指定弹性项目的 初始大小 ,即在flex-grow或flex-shrink影响它之前,它默认的宽度(在flex-direction: row时)或高度(在flex-direction: column时)。 -
它类似于
width,但在flexbox布局下有更高的优先级。
| 属性 | 作用范围 | 受 Flexbox 影响 | 默认值 | 优先级 |
|---|
width |
所有布局(block/inline/float/Flexbox) | ❌ 不受 flex-grow 和 flex-shrink 影响 |
auto |
低 (在 Flexbox 下优先级低于 flex-basis) |
|---|
flex-basis |
仅在 Flexbox 中生效 | ✅ 受 flex-grow 和 flex-shrink 影响 |
auto |
高 (如果 flex-basis 和 width 都设置,flex-basis 优先) |
|---|
flex简写属性
flex 简写属性 提供了一种方便的方式来指定元素如何 伸展(grow) 和 收缩(shrink) ,同时简化所需的 CSS 代码。
使用 flex 属性可以在 一行代码 中声明 flex-grow、flex-shrink 和 flex-basis。
注意 :flex 属性与 display: flex 中的 flex 值不同。
css
.big {
flex-grow: 2;
flex-shrink: 1;
flex-basis: 150px;
}
.small {
flex-grow: 1;
flex-shrink: 2;
flex-basis: 100px;
}
-
big类的元素 比small类的元素增长得更快 ,因为flex-grow: 2。 -
small类的元素 比big类的元素收缩得更快 ,因为flex-shrink: 2。
简写:
css
.big {
flex: 2 1 150px;
}
.small {
flex: 1 2 100px;
}
flex 属性的顺序 : 1️⃣ flex-grow
2️⃣ flex-shrink
3️⃣ flex-basis
- 省略flex-basis
css
.big {
flex: 2 1;
}
这里省略了 flex-basis ,它的默认值是 auto(基于元素内容或 width)。
- 省略
flex-shrink
css
.small {
flex: 1 20px;
}
这里省略了 flex-shrink ,但指定了 flex-basis: 20px。
-
你可以设置
flex-grow + flex-basis(如1 20px)。 -
但不能 只设置
flex-shrink + flex-basis,因为flex的第二个值如果是px,浏览器会认为它是flex-basis。
在浏览器中:
-
窗口变宽:
- 当 顶部外侧的 div 宽度达到 100px 后,它们比中间的 div 增长得更快。
- 当 底部中间的 div 宽度达到 100px 后,它比外侧 div 增长得更快。
-
窗口变窄:
- 当 顶部中间的 div 缩小到 50px 后,它开始 比外侧 div 收缩得更快。
- 当 底部外侧的 div 缩小到 75px 后,它们开始 比中间的 div 收缩得更快。
flex-wrap
有时候,我们不希望内容缩小以适应容器,而是希望 当空间不足时,子元素能够换行 。
这可以通过 flex-wrap 属性 来实现。
flex-wrap 的取值
| 值 | 作用 |
|---|---|
wrap |
换行 :当子元素无法放入同一行时,它们会 自动换到下一行。 |
wrap-reverse |
反向换行 :与 wrap 类似,但 行的顺序是反向的(例如,原本在上面的行会变到下面)。 |
nowrap |
不换行 (默认值):所有子元素会 强制保持在同一行,即使空间不足也不会换行。 |
html
<div class="container">
<div class="item">
<h1>We're going to wrap!</h1>
</div>
<div class="item">
<h1>We're going to wrap!</h1>
</div>
<div class="item">
<h1>We're going to wrap!</h1>
</div>
</div>
css
.container {
display: inline-flex;
flex-wrap: wrap;
width: 250px;
}
.item {
width: 100px;
height: 100px;
}
-
.container容器 宽度是250px。 -
.item元素 每个100px宽 ,三个item共 300px ,超过了250px的容器宽度。 -
由于
flex-wrap: wrap;设置,第三个item会自动换行,出现在下面一行。 -
flex-wrap必须在 flex 容器上声明 ,而 不能用于单个 flex 子元素。
效果:
如果给.container加上justify-content: space-around
align-content 属性
当元素 换行(flex-wrap: wrap;) 后,一个 Flex 容器可能会有多行子元素。
在之前的学习中,我们使用了 align-items 来 垂直对齐 单行 的 flex 项目。
如果 flex 容器 有多行内容 ,可以使用 align-content 来调整各行在容器内的上下间距。
align-content 的常见取值
| 值 | 作用 |
|---|---|
flex-start |
所有行都靠近父容器的顶部,行与行之间没有额外间距。 |
flex-end |
所有行都靠近父容器的底部,行与行之间没有额外间距。 |
center |
所有行居中排列,行与行之间没有额外间距。 |
space-between |
行与行之间均匀分布 ,第一行在顶部,最后一行在底部,行之间的间距相等。 |
space-around |
行均匀分布 ,顶部、底部和行之间的间距相等。 |
stretch(默认) |
如果未设置 height,所有行将拉伸以填充整个父容器的高度。 |
代码示例:
html
<div class="container">
<div class="child">
<h1>1</h1>
</div>
<div class="child">
<h1>2</h1>
</div>
<div class="child">
<h1>3</h1>
</div>
<div class="child">
<h1>4</h1>
</div>
</div>
css
.container {
display: flex;
width: 400px;
height: 400px;
flex-wrap: wrap;
align-content: space-around;
}
.child {
width: 150px;
height: 150px;
}
-
.container宽度为400px,但 每个.child宽150px,最多只能放 两个child在同一行。 -
因此,会换行形成两行。
-
align-content: space-around;使 两行均匀分布在容器内: -
顶部、底部和两行之间的间距相等(即,行间距是顶部和底部间距的两倍)。
-
align-content只适用于flex-wrap: wrap;的flex容器 ,如果nowrap(默认),这个属性无效。 -
它对单行无效 ,单行的垂直对齐应该使用
align-items。
效果:
flex-direction 属性
到目前为止,我们只讨论了 水平排列(横向)和垂直换行(纵向)的 flex 项目。
前面提到过,flex 容器有两个轴:
- 主轴(main axis) :默认是 水平方向 ,用于控制
justify-content、flex-wrap、flex-grow和flex-shrink等属性的表现。 - 交叉轴(cross axis) :默认是 垂直方向 ,用于控制
align-items和align-content等属性的表现。
主轴和交叉轴是 可以互换的 ,我们可以使用 flex-direction 属性来改变它们的方向。
例如,如果 flex-direction 的值是 column,那么 主轴将变为垂直方向 ,所有 flex 项目将按照 从上到下 的顺序排列,而不是默认的 从左到右。
html
<div class="container">
<div class="item">
<h1>1</h1>
</div>
<div class="item">
<h1>2</h1>
</div>
<div class="item">
<h1>3</h1>
</div>
<div class="item">
<h1>4</h1>
</div>
<div class="item">
<h1>5</h1>
</div>
</div>
css
.container {
display: flex;
flex-direction: column;
width: 1000px;
}
.item {
height: 100px;
width: 100px;
}
-
默认情况下,这 5 个
div可以在同一行内水平排列 ,但flex-direction: column;强制它们按照垂直方向排列。 -
justify-content和align-items的行为会受到flex-direction影响,例如:- 如果
flex-direction: row;,justify-content影响 水平方向 ,align-items影响 垂直方向。 - 如果
flex-direction: column;,justify-content影响 垂直方向 ,align-items影响 水平方向。
- 如果
效果:
将container(父元素)的height:600px改为height:600px后:
使用flex布局时,子元素会在max-height约束下自适应分配空间
-
默认情况下,
max-height限制的是父级的最大高度,子元素会自然适应。 -
如果子元素
height是固定的,则不会随父级max-height变化,可能会溢出或浪费空间。
将父元素的align-items设置为center后:
flex-flow
像 flex 这样的简写属性一样,flex-flow 也是一个简写属性,用于同时声明 flex-wrap 和 flex-direction 属性。
css
.container {
display: flex;
flex-wrap: wrap;
flex-direction: column;
}
在上面的示例中,我们用了两行代码来实现的效果,其实可以用一行代码完成:
css
.container {
display: flex;
flex-flow: column wrap;
}
在上面的示例中,flex-flow 声明的第一个值是 flex-direction,第二个值是 flex-wrap。所有 flex-direction 和 flex-wrap 的取值都可以用于 flex-flow。
注意: flex-flow 属性是应用在 flex 容器 上的。
嵌套flexbox
到目前为止,我们在同一页面上使用了多个 flex 容器来探索 flex 项目的定位。实际上,我们也可以在 flex 容器内部再嵌套 flex 容器。
html
<div class='container'>
<div class='left'>
<img class='small' src='#'/>
<img class='small' src='#'/>
<img class='small' src='#' />
</div>
<div class='right'>
<img class='large' src='#' />
</div>
</div>
css
.container {
display: flex;
justify-content: center;
align-items: center;
}
.left {
display: inline-flex;
flex: 2 1 200px;
flex-direction: column;
}
.right {
display: inline-flex;
flex: 1 2 400px;
align-items: center;
}
.small {
height: 200px;
width: auto;
}
.large {
height: 600px;
width: auto;
}
在上面的示例中,左侧的 div (.left)内包含三张小图片,这些图片会 从上到下排列 。右侧的 div (.right)包含一张大图片,位于页面的右侧。
.left的flex-basis较小 ,但在额外空间分配时会 伸展得更多。.right的flex-basis较大 ,但在额外空间分配时会 伸展得较少。- 这两个 div 既是 flex 容器,又是 flex 项目。
- 它们的 父容器(
.container) 定义了它们的排列方式,而 它们自身也可以影响内部 flex 子元素的排列方式。
我们将使用相同的格式来布局右侧的简单页面。
你已经学会了 Flexbox 最重要的属性。Flexbox 既是一门艺术,也是一门科学;你可以用它轻松地布局多个元素。现在,你已经掌握了使用 Flexbox 进行布局所需的一切知识,并可以在自己的项目中开始使用它了。
display: flex将元素变成一个 块级 容器,并且其子元素会成为 Flex 项目。display: inline-flex允许多个 Flex 容器在 同一行 内并排显示。justify-content用于 沿主轴 排列和分布子元素。align-items用于 沿交叉轴 进行对齐。flex-grow用于指定 子元素如何按比例扩展 以填充主轴上的剩余空间。flex-shrink用于指定 子元素在主轴上如何按比例缩小。flex-basis用于指定 子元素的初始大小 ,它与flex-grow和flex-shrink共同决定元素的最终尺寸。flex是flex-grow、flex-shrink和flex-basis的简写,可在一个声明中设置多个属性。flex-wrap指定 如果 Flex 容器空间不足 ,子元素是否应该 换行。align-content用于 沿交叉轴 调整 多行 内容的间距。flex-direction用于指定 主轴和交叉轴的方向。flex-flow是flex-wrap和flex-direction的简写,可以在 一行代码 中同时设置这两个属性。- 嵌套 Flex 容器 :可以在 Flex 容器的子元素 上 再次声明
display: flex或display: inline-flex,从而实现 嵌套布局。
现在,让我们应用你学到的一些属性,在浏览器中排列网页的某个部分吧!