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
,从而实现 嵌套布局。
现在,让我们应用你学到的一些属性,在浏览器中排列网页的某个部分吧!