CSS 基础
取值单位
外观样式
布局样式
动画样式
工具与规范
实例练习
前言
弹性盒子(Flexible Box,通常简称为 flexbox)布局是一种一维的布局模型。它的主要特点是给子元素提供了强大的空间分布和对齐能力。
$$tip
flex
布局已被浏览器全面支持,加上其强大的布局能力,现在网络上技术栈较新的网站都大量采用 flex
布局。比如:b 站、知乎等等。因此对于 flex
的知识点要求熟练掌握。
$$
基础概念和术语
由于 flexbox 一个整套布局方案的代称,并不单指某个样式属性。整个方案里面会涉及到很多东西,先通过下图进行理解有助于之后的学习。
- 容器(flex container):容纳所有 flex 子项的父元素被称为 flex 容器,容器的 display 样式为 flex 或 inline-flex。
- 子项(flex item):容器的第一级子元素,会被进行分布或排列对齐。
- 主轴(main axis):子项排列的轴向被称为主轴。
- 交叉轴(cross axis):与主轴垂直的轴向被称为交叉轴。
- 主轴长度(main size):主轴的长度。
- 交叉轴长度(cross size):交叉轴的长度。
Flexbox 布局讲解
在以下的例子中,我们创建了一个 flex 容器,而且没有添加任何属性配置。它有着三个子项,这些子项安静地排成了一行。这便是一个最简单的 flex
布局,以下学习的所有属性配置不过是调整子项的排列对齐方式。
$$tip
块级元素作为 flex 子项时宽度会等于内容的宽度。
$$
<iframe height="300" style="width: 100%" scrolling="no" title="flexbox" src="https://codepen.io/3yya/embed/ZEabGdz?default-tab=html%2Cresult&editable=true&theme-id=light" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"></iframe>
常用场景
垂直居中
垂直居中是一个很常见的应用,参见登录框垂直对齐实例。同时元素垂直居中一直是一个难点,而弹性盒子的出现很轻松地解决了这个问题。
以下实例中设置容器的 align-items: center 使得子项在交叉轴上居中。
<div class="container">
<div class="box"></div>
</div>
<style>
.container {
height: 200px;
background-color: pink;
display: flex;
align-items: center;
}
.box {
width: 50px;
height: 50px;
background-color: teal;
}
</style>
$$demo
<div class="container">
<div class="box"></div>
</div>
<style>
.container {
height: 200px;
background-color: pink;
display: flex;
align-items: center;
}
.box {
width: 50px;
height: 50px;
background-color: teal;
}
</style>
$$
垂直居中对齐
之前的代码中都是设置待对齐元素 vertical-align: middle;
的方式对行内元素实现垂直居中对齐,略显麻烦。
以下实例中设置容器的 align-items: center 使得子项在交叉轴上居中。
<div class="container">
<div class="box"></div>
<div class="box2"></div>
<div class="box3"></div>
</div>
<style>
.container {
height: 200px;
background-color: pink;
display: flex;
align-items: center;
}
.box {
width: 50px;
height: 50px;
background-color: teal;
}
.box2 {
width: 100px;
height: 150px;
background-color: green;
}
.box3 {
width: 80px;
height: 100px;
background-color: yellow;
}
</style>
$$demo
<div class="container">
<div class="box"></div>
<div class="box2"></div>
<div class="box3"></div>
</div>
<style>
.container {
height: 200px;
background-color: pink;
display: flex;
align-items: center;
}
.box {
width: 50px;
height: 50px;
background-color: teal;
}
.box2 {
width: 100px;
height: 150px;
background-color: green;
}
.box3 {
width: 80px;
height: 100px;
background-color: yellow;
}
</style>
$$
水平居中
在之前经常使用以下方式对元素水平居中:
- 行内元素:父元素设置
text-align: center;
- 块级元素:设置
margin: 0 auto;
现在则有了以下的方式,通过设置容器的 justify-content: center 可以让子项在主轴上居中排列。
$$tip
flex-direction: row 时主轴为水平方向, flex-direction: column 时主轴为垂直方向。 $$
<div class="container">
<div class="box"></div>
<div class="box2"></div>
</div>
<style>
.container {
background-color: pink;
display: flex;
justify-content: center;
}
.box {
width: 50px;
height: 50px;
background-color: teal;
}
.box2 {
width: 100px;
height: 50px;
background-color: yellow;
}
</style>
$$demo
<div class="container">
<div class="box"></div>
<div class="box2"></div>
</div>
<style>
.container {
background-color: pink;
display: flex;
justify-content: center;
}
.box {
width: 50px;
height: 50px;
background-color: teal;
}
.box2 {
width: 100px;
height: 50px;
background-color: yellow;
}
</style>
$$
两端对齐
两端对齐是最常见的场景之一,导航栏经常就是两端对齐, 参见一个导航栏两端对齐的实例。一个之前介绍过使用 float
实现两端对齐的方式,但使用 flex
实现起来将会更加优雅。
以下通过设置容器的 justify-content: space-between 使其实现了主轴两端对齐。
<div class="container">
<div class="box"></div>
<div class="box2"></div>
</div>
<style>
.container {
background-color: pink;
display: flex;
justify-content: space-between;
}
.box {
width: 50px;
height: 50px;
background-color: teal;
}
.box2 {
width: 100px;
height: 50px;
background-color: teal;
}
</style>
$$demo
<div class="container">
<div class="box"></div>
<div class="box2"></div>
</div>
<style>
.container {
background-color: pink;
display: flex;
justify-content: space-between;
}
.box {
width: 50px;
height: 50px;
background-color: teal;
}
.box2 {
width: 100px;
height: 50px;
background-color: teal;
}
</style>
$$
弹性伸缩
有时需要某个元素占满剩余的空间,比如这个弹性搜索框实例。
<div class="container">
<div class="box"></div>
<div class="flex-box"></div>
<div class="box2"></div>
</div>
<style>
.container {
display: flex;
}
.box {
width: 50px;
height: 50px;
background-color: teal;
}
.flex-box {
flex: 1;
background-color: yellow;
}
.box2 {
width: 100px;
height: 50px;
background-color: teal;
}
</style>
$$demo
<div class="container">
<div class="box"></div>
<div class="flex-box"></div>
<div class="box2"></div>
</div>
<style>
.container {
display: flex;
}
.box {
width: 50px;
height: 50px;
background-color: teal;
}
.flex-box {
flex: 1;
background-color: yellow;
}
.box2 {
width: 100px;
height: 50px;
background-color: teal;
}
</style>
$$
容器属性
容器属性部分包括了所有跟 flex 有关的作用于容器的属性样式。
display
定义 flex 容器。
- flex:定义元素为弹性盒子容器。
- inline-flex:定义元素为行内弹性盒子容器。
$$tip
flex
与块级元素类似,特性如下:
- 默认占满父元素内容区域。
- 独占一行。
inline-flex
与行内块元素类似,特性如下:
- 默认宽度为内容宽度。
- 可与其他行内元素共处一行。
$$
修改以下样例的 display
属性,观察下窗口背景色的宽度理解它们的区别。
<iframe height="300" style="width: 100%" scrolling="no" title="flexbox" src="https://codepen.io/3yya/embed/MWOaaZb?default-tab=css%2Cresult&editable=true&theme-id=light" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"></iframe>
justify-content
定义了子项沿主轴的对齐方式,有以下常用属性值:
- flex-start(默认):起始线对齐。
- flex-end:终止线对齐。
- center:居中对齐。
- space-between:子项均匀分布,两端对齐。
- space-around:子项均匀分布,两端间距与其余间距一半。
- space-evenly:子项均匀分布,两端间距为其余间距一致。
align-items
定义子项在交叉轴上的对齐方式,有以下取值:
- stretch:默认值,未设置尺寸的元素将被拉伸至交叉轴长度(cross size)。
- flex-start:交叉轴开始处(cross start)对齐。
- flex-end:交叉轴结束处(cross end)对齐。
- center:交叉轴中部对齐。
- baseline:交叉轴上基线对齐。
flex-direction
决定了主轴的方向。
- row(默认):水平方向。
- row-reverse:水平方向,反向。
- column:垂直方向。
- column-reverse:垂直方向,反向。
以下实例设置了 flex-direction
为 row-reverse
,可见子项元素是从右至左排列的。试着修改
flex-direction
为其他值看看效果。
<iframe height="300" style="width: 100%" scrolling="no" title="flexbox" src="https://codepen.io/3yya/embed/WNXQQBQ?default-tab=css%2Cresult&editable=true&theme-id=light" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"></iframe>
flex-wrap
定义子项目在超过一行时是否换行。
- nowrap(默认值):不换行。
- wrap:子项将会换行,从上至下。
- wrap-reverse:子项将会换行,从下至上。
以下实例将 flex-wrap
设置为 wrap-reverse
,可以看到是从下至往上排列。
<iframe height="300" style="width: 100%" scrolling="no" title="flexbox" src="https://codepen.io/3yya/embed/dyZYGNa?default-tab=css%2Cresult&editable=true&theme-id=light" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"></iframe>
flex-flow
为 flex-direction
和 flex-wrap
的合并写法。
例: flex-flow: column wrap;
子项属性
order
默认情况下,子项顺序按照元素的顺序排列,也可以用 order 改变它们的顺序。默认子项的 order
为 0
,按从小到大的顺序排序。
<iframe height="300" style="width: 100%" scrolling="no" title="flexbox" src="https://codepen.io/3yya/embed/xxPwVpb?default-tab=css%2Cresult&editable=true&theme-id=light" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"></iframe>
flex-grow
定义子项的弹性增长权重,决定了在主轴上存在剩余空间时,子项增长的权重系数,默认值为 0
。
计算公式:
元素长度 = 设定长度 + 增长长度
增长长度 = 剩余长度 * 权重占比
权重占比 = 自身权重 / 总权重
自身权重 = flex-grow
得到:元素长度 = 设定长度 + 剩余长度 * flex-grow / 总权重
假设:存在 a、b 两个子项目,主轴空间剩余 100px。
a:
flex-grow:3;
width: 100px;
总权重 = 3 + 1(b 的权重)= 4
权重占比 = 3/4
元素长度 = 100px(设定长度)+ 100px(剩余长度)* 3/4(权重占比) = 175px
b:
flex-grow:1;
width: 200px;
总权重 = 1 + 3(a 的权重)= 4
权重占比 = 1/4
元素长度 = 200px(设定长度)+ 100px(剩余长度)* 1/4(权重占比) = 225px
<div class="container">
<div class="a"></div>
<div class="b"></div>
</div>
<style>
.container {
display: flex;
width: 400px;
height: 100px;
background-color: gray;
}
.a {
width: 100px;
background-color: yellow;
flex-grow: 3;
}
.b {
width: 200px;
background-color: teal;
flex-grow: 1;
}
</style>
$$demo
<div class="container">
<div class="a"></div>
<div class="b"></div>
</div>
<style>
.container {
display: flex;
width: 400px;
height: 100px;
background-color: gray;
}
.a {
width: 100px;
background-color: yellow;
flex-grow: 3;
}
.b {
width: 200px;
background-color: teal;
flex-grow: 1;
}
</style>
$$
flex-shrink
定义子项的弹性收缩权重,决定了在主轴上发生溢出时,子项收缩的权重系数,默认值为 1
。与 flex-grow 不同的是,权重的计算还与自身设定的长度有关。
计算公式:
元素长度 = 设定长度 - 收缩长度
收缩长度 = 溢出长度 * 权重占比
权重占比 = 自身权重 / 总权重
自身权重 = 设定长度 * flex-shrink
得到:元素长度 = 设定长度 - 溢出长度 * (设定长度 * flex-shrink)/ 总权重
假设:存在 a、b 两个子项目,主轴空间溢出 100px。
a:
flex-shrink:6;
width:100px;
自身权重 = 100(设定长度)* 6(flex-shrink)= 600
总权重 = 600 + 400(b 的权重)= 1000
权重占比 = 600 / 1000 = 3/5
元素长度 = 100px(设定长度) - 100px(溢出长度) * 3/5(权重占比) = 40px
b:
flex-shrink:1;
width:400px;
自身权重 = 400(设定长度)* 1(flex-shrink)= 400
总权重 = 400 + 600(a 的权重)= 1000
权重占比 = 400 / 1000 = 2/5
元素长度 = 400px(设定长度) - 100px(溢出长度) * 2/5(权重占比) = 360px
<div class="container">
<div class="a"></div>
<div class="b"></div>
</div>
<style>
.container {
display: flex;
width: 400px;
height: 100px;
background-color: gray;
}
.a {
width: 100px;
background-color: yellow;
flex-shrink: 6;
}
.b {
width: 400px;
background-color: teal;
flex-shrink: 1;
}
</style>
$$demo
<div class="container">
<div class="a"></div>
<div class="b"></div>
</div>
<style>
.container {
display: flex;
width: 400px;
height: 100px;
background-color: gray;
}
.a {
width: 100px;
background-color: yellow;
flex-shrink: 6;
}
.b {
width: 400px;
background-color: teal;
flex-shrink: 1;
}
</style>
$$
$$warning
默认情况下 flex-shrink
不能收缩小于内容于主轴上的长度。如果要使其收缩小于内容的长度,需要设置以下值:
min-width: 0
(主轴为水平方向时)min-height: 0
(主轴为垂直方向时)overflow: hidden
(或任何其他值,除了visible
)
参考资料:Why don't flex items shrink past content size?
$$
<iframe height="300" style="width: 100%" scrolling="no" title="flexbox" src="https://codepen.io/3yya/embed/jOYbJJG?default-tab=css%2Cresult&editable=true&theme-id=light" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true"></iframe>
flex-basis
定义了子项在主轴上的默认大小,当主轴是水平方向时 flex-basis
约等于 width
, 当主轴是垂直方向时 flex-basis
约等于 height
。
flex
flex-grow
、 flex-shrink
、 flex-basis
的组合写法。默认值为 0 1 auto
。
align-self
定义单个子项的对齐方式,有以下取值:
- auto:默认值,跟随容器
align-items
的定义。 - flex-start:交叉轴开始处(cross start)对齐。
- flex-end:交叉轴结束处(cross end)对齐。
- center:交叉轴中部对齐。
- baseline:交叉轴上基线对齐。