持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情
一、认识Flex布局
1-1、认识FlexBox
FlexBox翻译为弹性盒子:
- 弹性盒子是一种用于按行或按列布局元素的一维布局方案
- 元素可以膨胀以填充额外的空间,收缩以适应更小的空间
- 通常我们使用FlexBox来进行布局的方案被称为flex布局(flex layout)
flex布局是目前web开发中使用最多的布局方案:
- 特别是在移动端可以说是已经完全普及
- PC端也几乎已经完全普及和使用,只有非常少的网站依然在使用浮动布局(为了适配低版本浏览器)
为什么需要flex布局:
- 长久以来,CSS布局中唯一可靠且跨浏览器兼容的布局只有浮动与定位
- 但是这两种方案本身存在很大的局限性,并且使用这两种方案布局是无奈之举
- 所以大家一直期待一种真正用于对元素布局的方案,于是flex布局出现了
1-2、认识Flex Container & Flex Item
- 开启了flex布局的元素叫做flex container
- flex container里面的直接子元素叫做flex item (注意,这里是直接子元素,孙子元素以及其他层级的子元素不算~)
当flex container中的子元素变成了flex item时,具备以下特点:
- flex item的布局将受flex container属性的设置来进行控制和布局
- flex item不再严格区分块级元素和行内级元素
- flex item默认情况下是包裹内容的,但是可以设置宽度和高度
那么如何成为Flex Container呢?
-
为该盒子设置display为flex / inline-flex即可
- 设置为flex:flex container将以块级元素形式存在
- 设置为inline-flex:flex container将以行内元素的形式存在
1-3、Flex Container相关属性
1-4、Flex Item相关属性
二、认识主轴与交叉轴
2-1、主轴
默认情况下主轴位于水平方向,方向从左至右
- 左侧为主轴的起始位置(main start)
- 右侧为主轴的结束位置(main end)
2-2、交叉轴
默认情况下交叉轴位于垂直方向,方向从上至下
- 上方为交叉轴的起始位置(cross start)
- 下方为交叉轴的结束位置(cross end)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
}
.main div {
width: 100px;
height: 100px;
background: pink;
font-size: 20px;
}
</style>
</head>
<body>
<div class="main">
<div>1</div>
<div>2</div>
<div>3</div>
</div>
</body>
</html>
2-3、改变轴方向
通过在Felxbox中设置flex-direction属性改变:
- flex-direction: row-reverse;
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
flex-direction: row-reverse;
}
- flex-direction: column;
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
flex-direction: column;
}
- flex-direction: column-reverse;
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
flex-direction: column-reverse;
}
三、关于FlexItem换行
3-1、Flex Item宽度大于Flex Container时
在上述例子中,Flex Container的宽度为100px,Flex Item的宽度为100px,当Flex Item的总宽度超过Flex Container的宽度会怎样呢?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
}
.main div {
width: 100px;
height: 100px;
background: pink;
font-size: 20px;
}
</style>
</head>
<body>
<div class="main">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
</div>
</body>
</html>
可见,Flex Item不会发生换行,而是宽度变窄以适应Flex Container的宽度。
当Flex Item更多时:
当Flex Item收缩将不足以满足内容宽度时:
可见:
- 当Flex Item的总宽度大于Flex Container宽度时,Flex Item的宽度将收缩以适应Flex Container的宽度
- 当Flex Item的宽度缩小到内容宽度时,将不再收缩,最终会溢出Flex Container,依旧不会换行
3-2、换行属性
可以通过为Flex Container设置flex-wrap属性来实现Flex Item的换行效果:
- flex-wrap: wrap;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
flex-wrap: wrap;
}
.main div {
width: 100px;
height: 100px;
background: pink;
font-size: 20px;
}
</style>
</head>
<body>
<div class="main">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
</div>
</body>
</html>
可见,此时Flex Item总宽度为700px 大于Flex Container的宽度,设置了flex-wrap: wrap后Flex Item会发生换行,但是换行后,第二行元素并不是紧挨着第一行元素的:
- 由于此时会有两行,会将Flex Container的高度平均分为两份,每一行所占位置为平均分配的这一份的最顶端
- 同理,当有3行时会将Flex Container的高度平均分为三份,每一行所占位置为每一份的最顶端:
- 那么如果Flex Container没有设置高度时,每一行将会挨在一起:
.main {
width: 500px;
/* height: 500px; */
background: skyblue;
display: flex;
flex-wrap: wrap;
}
- flex-wrap: wrap-reverse;
通过设置 flex-wrap: wrap-reverse;可使得Flex Item进行反向换行
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
flex-wrap: wrap-reverse;
}
.main div {
width: 100px;
height: 100px;
background: pink;
font-size: 20px;
}
</style>
</head>
<body>
<div class="main">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
</div>
</body>
</html>
3-3、换行属性与改变轴方向属性结合
默认情况下主轴方向为水平方向,所以换行表现为水平方向的折行效果,当通过flex-direction改变了主轴方向后,换行变现方式也会发生改变:
- 当把主轴方向改变为垂直方向时
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
flex-direction: column;
flex-wrap: wrap;
}
此时会将Flex Container的宽度均分为两等份,Flex Item将进行垂直方向上的换行
- 可以使用flex-flow属性对flex-direction与flex-wrap进行简写:
- 语法:flex-flow: [flex-direction] [flex-wrap]
- 可以先写方向然后写换行,也可以互换顺序,两者均可以省略
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
/* flex-direction: column;
flex-wrap: wrap; */
flex-flow: column wrap;
}
3-4、Flex Item特点
以主轴方向为水平方向为例子:
当不给Flex Item设置宽度时:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
}
.main div {
/* width: 100px; */
height: 100px;
background: pink;
font-size: 20px;
}
</style>
</head>
<body>
<div class="main">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
<div>10</div>
</div>
</body>
</html>
此时,Flex Item的宽度将由内容宽度决定,并不会自适应Flex Container的宽度
那么当不给Flex Item设置高度呢?
.main div {
/* width: 100px; */
/* height: 100px; */
background: pink;
font-size: 20px;
}
此时,Flex Item的高度将与Flex Container高度一致
ps: 同理,当主轴方向为垂直方向时,如果不给flex item设置宽度,则flex item的宽度将与flex容器宽度一致;不给flex item设置高度时,高度将由其内容决定:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
flex-direction: column;
}
.main div {
/* width: 100px; */
/* height: 100px; */
background: pink;
font-size: 20px;
}
</style>
</head>
<body>
<div class="main">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
<div>10</div>
</div>
</body>
</html>
四、主轴对齐方式
4-1、相关属性值介绍
通过为Flex Container设置justify-content属性可改变Flex Item在主轴方向上的对齐方式:
4-2、使用介绍(以主轴为水平方向为例)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
}
.main div {
width: 100px;
height: 100px;
background: pink;
font-size: 20px;
}
</style>
</head>
<body>
<div class="main">
<div>1</div>
<div>2</div>
<div>3</div>
</div>
</body>
</html>
- 默认情况下,主轴方向的排列方式为flex-start,此时由于主轴方向为水平方向,Flex Item将会沿着主轴方向(水平方向)紧挨着,靠着主轴的开始方向对齐:
- 当设置justify-content: flex-end时,Flex Item将会沿着主轴方向(水平方向)紧挨着,靠着主轴的结束方向对齐:
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
justify-content: flex-end;
}
- 当设置justify-content: center时,Flex Item将会沿着主轴方向(水平方向)紧挨着,并在主轴方向上居中显示:
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
justify-content: center;
}
- 当设置justify-content: space-around时,Flex Item将会沿着主轴方向以平均分配左右空间的方式分散排开:
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
justify-content: space-around;
}
(PS:第一个子元素的右边的空间与第二个子元素的左边的空间是相同的,第二个子元素的右边的空间与第三个子元素的左边的空间是相同的,所以中间元素的左右空间会是第一个子元素左边空间、最后一个子元素右边空间的2倍)
- 当设置justify-content: space-between时,Flex Item中的第一个元素与最后一个元素将分别贴着主轴方向的开始位置与结束为止,其余元素将平均分配剩余空间:
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
justify-content: space-between;
}
- 当设置justify-content: space-evenly时,以主轴方向为水平方向为例,所有Flex Item元素的左侧与右侧空间将会平均分配:
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
justify-content: space-evenly;
}
所以,与space-around不同的是,space-evenly会使得所有子元素的左右两侧空间相等,而不会造成中间元素两侧空间会比两边元素空间大的情况。
4-3、注意事项
当Flex Item折行时
当折行时需要注意:
- 第一行子元素占满了flex容器,所以对于第一行来说justify-content会没有效果
- 其余没有占满一行的子元素将会按照justify-content的设置的方式进行排列
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
flex-wrap: wrap;
justify-content: space-evenly;
}
.main div {
width: 100px;
height: 100px;
background: pink;
font-size: 20px;
}
</style>
</head>
<body>
<div class="main">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
</div>
</body>
</html>
当改变主轴方向时
当改变主轴方向时,比如主轴方向变为垂直方向,那么子元素会在垂直方向上进行相应的排布方式:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
flex-direction: column;
justify-content: space-evenly;
}
.main div {
width: 100px;
height: 100px;
background: pink;
font-size: 20px;
}
</style>
</head>
<body>
<div class="main">
<div>1</div>
<div>2</div>
<div>3</div>
</div>
</body>
</html>
五、交叉轴对齐方式
通过为Flex Container设置align-content / align-items 属性可改变Flex Item在交叉轴方向上的对齐方式
5-1、align-content
该属性决定了多行的Flex Item在交叉轴上的对齐方式,与justify-content用法相似
注意事项
需要注意的是align-content在子元素没有折行的情况下是不生效的,该属性针对的是多行的Flex Item在交叉轴上的排列方式:
- 只要为flex容器设置了flex-wrap: wrap,不管此时是否只有一行子元素,align-content属性就会生效
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
align-content: flex-end;
}
.main div {
width: 100px;
height: 100px;
background: pink;
font-size: 20px;
}
</style>
</head>
<body>
<div class="main">
<div>1</div>
<div>2</div>
<div>3</div>
</div>
</body>
</html>
此时子元素只有一行,为flex容器设置align-content: flex-end可以发现是不生效的:
如果为flex容器设置了flex-wrap的话,align-content就会生效(即使此时只有一行子元素):
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
flex-wrap: wrap;
align-content: flex-end;
}
使用介绍(以交叉轴为垂直方向为例)
- align-content: stretch;
align-content的默认值为stretch,效果为拉伸Flex Item的高度与flex容器高度一致,但是需要注意的是,默认值的拉伸效果是在不为Flex Item设置高度时才可以看出来:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
flex-wrap: wrap;
}
.main div {
width: 100px;
/* height: 100px; */
background: pink;
font-size: 20px;
}
</style>
</head>
<body>
<div class="main">
<div>1</div>
<div>2</div>
<div>3</div>
</div>
</body>
</html>
如果此时为align-content设置了其他属性值,那么Flex Item的高度便会以内容高度自适应(如果Flex Item没有设置高度的话):
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
}
- align-content: flex-start;
此时每一行的FlexItem会挨在一起分布在flex容器的顶部
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
}
.main div {
width: 100px;
height: 100px;
background: pink;
font-size: 20px;
}
</style>
</head>
<body>
<div class="main">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
</div>
</body>
</html>
- align-content: flex-end;
此时每一行的FlexItem会挨在一起分布在flex容器的底部
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
flex-wrap: wrap;
align-content: flex-end;
}
- align-content: center;
此时每一行的FlexItem会挨在一起分布在flex容器的中间
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
flex-wrap: wrap;
align-content: center;
}
- align-content: space-around;
此时每一行的Flex Item会均分上方与下方的空间,所以中间部分的Flex Item的上方与下方空间是两端Flex Item的两倍
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
flex-wrap: wrap;
align-content: space-around;
}
- align-content: space-between;
第一行与最后一行元素分别位于两端,中间行元素均分剩余空间
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
flex-wrap: wrap;
align-content: space-between;
}
.main div {
width: 100px;
height: 100px;
background: pink;
font-size: 20px;
}
</style>
</head>
<body>
<div class="main">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
</div>
</body>
</html>
- align-content: space-evenly;
每一行两端的空间都相等
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
flex-wrap: wrap;
align-content: space-evenly;
}
5-2、align-items
用于改变交叉轴方向上每一行子元素的对齐方式
注意事项
以交叉轴方向为垂直方向为例,对比align-content与align-items:
- 与align-content不同的是,align-items是针对的每一行的子元素在交叉轴上的对齐方式,而不是整体
以flex-start为例:
- align-content: flex-start
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
flex-wrap: wrap;
align-content: flex-start;
}
.main div {
width: 100px;
height: 100px;
background: pink;
font-size: 20px;
}
</style>
</head>
<body>
<div class="main">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
</div>
</body>
</html>
可见,所有行的子元素作为一个整体靠着flex容器的顶部对齐
- align-items: flex-start
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
flex-wrap: wrap;
align-items: flex-start;
}
此时,每一行的子元素是在所属行的顶部对齐
- align-items在不设置flex-wrap:wrap时也是可以生效的:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
align-items: center;
}
.main div {
width: 100px;
height: 100px;
background: pink;
font-size: 20px;
}
</style>
</head>
<body>
<div class="main">
<div>1</div>
<div>2</div>
<div>3</div>
</div>
</body>
</html>
使用介绍
- align-items: flex-start;
每一行子元素在所属行的顶部对齐
- align-items: flex-end;
每一行子元素在所属行的底部对齐
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
flex-wrap: wrap;
align-items: flex-end;
}
- align-items: stretch;
每一行子元素会在所属行拉伸,需要注意的是,Flex Item不设置高度的时候才会看出来效果:
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
flex-wrap: wrap;
align-items: stretch;
}
.main div {
width: 100px;
/* height: 100px;*/
background: pink;
font-size: 20px;
}
- align-items: center;
每一行子元素在所属行的中间对齐
.main {
width: 500px;
height: 500px;
background: skyblue;
display: flex;
flex-wrap: wrap;
align-items: center;
}
.main div {
width: 100px;
height: 100px;
background: pink;
font-size: 20px;
}
- align-items: baseline;
子元素沿着基线(小些字母"x"的底部)对齐
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main{
width:500px;
border:1px black solid;
display: flex;
align-items: baseline;
}
</style>
</head>
<body>
<div class="main">
xyz
<img src="./1.png" alt="">
</div>
</body>
</html>
六、FlexItem的排序
为FlexItem设置order属性可以改变某一个FlexItem的排序位置
- 默认为0
- 数字越大,排列位置越靠后
- 可以为负数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main{
width:500px;
height:500px;
background:skyblue;
display: flex;
align-items: center;
}
.main div{
width:100px;
height:100px;
background:pink;
}
.main div:nth-of-type(1){
order:1;
}
.main div:nth-of-type(4){
order:-1;
}
</style>
</head>
<body>
<div class="main">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
</div>
</body>
</html>
七、FlexItem扩展比例
为FlexItem设置flex-grow可用于改变FlexItem的扩展比例:
flex-grow的值为大于等于0的数,表示所占用空白间隙扩展自己的比例
- 默认值为0,表示不占用剩余的空间
FlexItem数量为1时
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main {
width: 500px;
height: 300px;
background-color: skyblue;
display: flex;
}
.main div {
width: 100px;
height: 100px;
background-color: pink;
flex-grow: 1;
}
</style>
</head>
<body>
<div class="main">
<div>1</div>
</div>
</body>
</html>
可见,此时div宽度占满了父容器:
打开控制台,鼠标放上去可以发现,箭头指向的区域表示扩展的宽度:
- 为FlexItem设置了flex-grow:1,就会占满剩余的所有空间(此时为100 + 400 * 1 = 500),那么设置为0.5呢?
-
- 可见如果设置为0.5,此时会扩展剩余空间一半的宽度:原本为100的宽度,父容器为500,剩余空间为400,此时会扩展400 * 0.5的宽度,所以最终会是100 + 400 * 0.5 = 300
- 如果Flex Item的flex-grow大于1时也会占满整个空间
.main div {
width: 100px;
height: 100px;
background-color: pink;
flex-grow: 2;
}
FlexItem数量大于1时
- 当为其中一个子元素设置flex-grow: 1时,其会扩展剩余的宽度为自己的宽度
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main{
width: 500px;
height: 300px;
background: skyblue;
display: flex;
}
.main div:nth-of-type(1){
width:200px;
height:100px;
background:seashell;
flex-grow: 1;
}
.main div:nth-of-type(2){
width:100px;
height:100px;
background:pink;
}
</style>
</head>
<body>
<div class="main">
<div>1</div>
<div>2</div>
</div>
</body>
</html>
第一个子元素原始宽度为200,第二个子元素宽度为100,剩余宽度为500 - 200 - 100 = 200,所以当为第一个子元素设置flex-grow: 1时,会将剩余的200px扩展为自己的宽度,所以最后它的宽度为 200 + 200 = 400
- 当所有子元素都设置了flex-grow时,那么扩展的宽度将成比例分配:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main{
width: 500px;
height: 300px;
background: skyblue;
display: flex;
}
.main div:nth-of-type(1){
width:200px;
height:100px;
background:seashell;
flex-grow: 1;
}
.main div:nth-of-type(2){
width:100px;
height:100px;
background:pink;
flex-grow: 3;
}
</style>
</head>
<body>
<div class="main">
<div>1</div>
<div>2</div>
</div>
</body>
</html>
剩余宽度为:500 - 200 - 100 = 200,此时第一个子元素flex-grow为1,第二个子元素的flex-grow为3,那么第一个子元素扩展的剩余宽度为: 200 * 1 / ( 1+ 3) = 50,第二个子元素扩展的剩余宽度为:200 * 3 / (1 + 3) = 150;所以第一个子元素的最终宽度为200 + 50 = 250,第二个子元素的最终宽度为:100 + 150 = 250
- 当所有子元素flex-grow之和小于1时,那么最终宽度 = 宽度 + 剩余宽度 * flex-grow值
.main{
width: 500px;
height: 300px;
background: skyblue;
display: flex;
}
.main div:nth-of-type(1){
width:200px;
height:100px;
background:seashell;
flex-grow: 0.1;
}
.main div:nth-of-type(2){
width:100px;
height:100px;
background:pink;
flex-grow: 0.3;
}
第一个子元素宽度为:200 + (500 - 200 - 100) * 0.1 = 220
第一个子元素宽度为:100 + (500 - 200 - 100) * 0.3 = 160
八、FlexItem收缩比例
通过为FlexItem设置flex-shrink可改变FlexItem的收缩比例:
其表示flex容器空间不足时,Flex Item的收缩比例
- 默认值为1
FlexItem数量为1时
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main{
width:500px;
height:200px;
background:skyblue;
display: flex;
}
.main div{
width:600px;
height:100px;
background:pink;
/* flex-shrink : 1; */
}
</style>
</head>
<body>
<div class="main">
<div>1</div>
</div>
</body>
</html>
可见。Flex Item此时与Flex容器等宽,检查模式下,鼠标放上去之后可以发现:
- Flex Item收缩了100px,由于Flex Item本身宽度为600px,超出了Flex容器宽度100px,那么默认情况下,自身将会收缩100px
- 如果将其flex-shrink设置为0,会发现此时不会进行收缩:
.main div{
width:600px;
height:100px;
background:pink;
flex-shrink : 0;
}
- 如果将其flex-shrink设置为小于1大于0的小数,则会按照比例进行收缩:
.main div{
width:600px;
height:100px;
background:pink;
flex-shrink : 0.5;
}
溢出宽度为100,收缩宽度 = 100 * 0.5 = 50,所以最终宽度为600 - 50 = 550
FlexItem数量大于1时
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main{
width:500px;
height:200px;
background:skyblue;
display: flex;
}
.main div:nth-child(1){
width:300px;
height:100px;
background:pink;
}
.main div:nth-child(2){
width:400px;
height:100px;
background:seashell;
}
</style>
</head>
<body>
<div class="main">
<div>1</div>
<div>2</div>
</div>
</body>
</html>
由于默认flex-shrink为1,两个元素都会进行收缩,但是由上可看出两个元素收缩的宽度不一样,究竟是如何收缩的呢?
- 两个元素总宽度为 300 + 400 = 700 ,溢出 700 - 500 = 200的宽度
- 收缩的宽度是根据两个元素宽度的占比所决定的
- 第一个元素宽度占比为 300 / (300 + 400) = 3 / 7,所以收缩宽度为 200 * 3/7,那么最终宽度为 300 - 200 * 3/7 = 214.28
- 第二个元素宽度占比为 400 / (300 + 400)= 4 / 7,所以收缩宽度为 200 * 4/7, 那么最终宽度为 400 - 200 * 4/7 = 285.72
那么如果两者flex-shrink值不一样呢?
.main div:nth-child(1){
width:300px;
height:100px;
background:pink;
flex-shrink: 2;
}
.main div:nth-child(2){
width:400px;
height:100px;
background:seashell;
flex-shrink: 1;
}
可见,此时第一个元素宽度为180,第二个元素宽度为320,此时是如何计算的呢?
-
两个元素总宽度为 300 + 400 = 700 ,溢出 700 - 500 = 200的宽度
-
收缩的宽度是根据两个元素的宽度以及两个元素flex-shrink值共同决定的:
- 由于第一个元素的flex-shrink为2,第二个元素的flex-shrink为1,所以此时第一个元素收缩占比为: 300 * 2 / 300 * 2 + 400 * 1 = 6 / 10 = 3 / 5 ;第二个元素收缩占比为:400 * 1 / 300 * 2 + 400 * 1 = 4 / 10 = 2 / 5
- 所以第一个元素最终宽度为 300 - 200 * 3/5 = 180;第二个元素最终宽度为400 - 200 * 2/5 = 320
总结 :
元素最终的宽度 = 元素本身宽度 - 溢出宽度 * (元素本身宽度 * 收缩比例)/ (元素1本身宽度 * 收缩比例 + 元素2本身宽度 * 收缩比例 + ... + 元素n本身宽度 * 收缩比例)
九、 FlexItem在主轴方向的初始大小
通过设置FlexItem的flex-basis属性可以指定flex item在主轴方向上的初始大小:
- 默认值为auto,将会以其中内容自适应
在主轴方向为水平方向的情况下,如果为FlexItem设置了flex-basis属性,那么其优先级将高于width:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main{
width:500px;
height:500px;
background:skyblue;
display: flex;
}
.main div{
width:100px;
height:100px;
background:pink;
flex-basis: 200px;
}
</style>
</head>
<body>
<div class="main">
<div></div>
</div>
</body>
</html>
如果将主轴方向改为垂直方向,那么Flex Item的flex-basis优先级将高于height:
.main{
width:500px;
height:500px;
background:skyblue;
display: flex;
flex-direction: column;
}
十、flex
flex属性是flex-grow、flex-shrink、flex-basis的缩写
flex属性可以指定1个、2个或3个值
单值
一个无单位的数会被当作flex-grow的值
一个有效的宽度会被当作flex-basis的值(如: flex: 100px 代表flex-basis: 100px)
除此之外还可以使用关键字none、auto
- flex: 1 对应的是:flex-grow: 1; flex-shrink: 1; flex-basis: 0%;
- flex: 0 对应的是: flex-grow: 0; flex-shrink: 1; flex-basis: 0%;
- flex: auto 对应的是:flex-grow: 1; flex-shrink:1; flex-basis: auto;
- flex: none 对应的是: flex-grow: 0; flex-shrink: 0; flex-basis: auto;
双值
使用双值语法时,第一个值必须是无单位的数,并且它会被当作flex-grow的值
第二个值必须是以下之一:
- 一个无单位的数:会被当作flex-shrink的值(如:flex: 1 1 代表 flex-grow: 1 flex-shrink: 1)
- 一个有效的宽度值:会被当作flex-basis的值(如:flex: 1 100px 代表 flex-grow: 1 flex-basis: 100px)
三值
那么如果写三个值的话就分别代表flex-grow、flex-shrink、flex-basis
十一、align-self
控制单独某一个FlexItem子项的交叉轴上的对齐方式
-
默认为auto,默认对应Flex容器中设置的align-items
- 比如flex容器中设置了align-items为center,那么该子元素的align-self默认也为center
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main{
width:500px;
height:500px;
background:skyblue;
display: flex;
align-items: center;
}
.main div{
width:100px;
height:100px;
background:pink;
}
.main div:nth-of-type(4){
order:-1;
height:50px;
align-self: flex-end;
}
</style>
</head>
<body>
<div class="main">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
</div>
</body>
</html>
十二、flex布局案例
12-1、块的上下左右居中布局
比如实现图片在一个盒子内的上下左右居中对齐,图片可能大小不一,对于这种内容不固定的情况下,如何实现居中呢?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
width: 300px;
height: 200px;
background: skyblue;
display: flex;
justify-content: center;
align-items: center;
}
.box div{
width:100px;
height:100px;
background:pink;
}
</style>
</head>
<body>
<div class="box">
<div></div>
</div>
</body>
</html>
12-2、不定项居中布局
比如,轮播图的指示按钮会由根据轮播图数量来决定,数目是不确定的,对于这种不定项该如何居中呢?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
width: 300px;
height: 150px;
background: skyblue;
display: flex;
justify-content: center;
align-items: flex-end;
}
.box div {
width: 30px;
height: 30px;
background: pink;
border-radius: 50%;
margin:5px;
}
</style>
</head>
<body>
<div class="box">
<div></div>
<div></div>
<div></div>
</div>
</body>
</html>
12-3、均分列布局
比如,实现导航中内容的均分:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main{
height:200px;
background:skyblue;
display: flex;
justify-content: space-between;
align-items: flex-end;
padding:0 20px;
}
.main div{
width:30px;
height:30px;
background:pink;
border-radius: 50%;
}
</style>
</head>
<body>
<div class="main">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</body>
</html>
12-4、子项分组布局
有时我们会遇到所有子项并不是都是均分的效果,有的在左有的在右
方案一
将第二个与第三个子元素外多套一个div,让第一个子元素与该div进行flex布局后,在该div中再进行flex布局:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main{
height:200px;
background: skyblue;
display: flex;
justify-content: space-between;
align-items: center;
}
.main div:nth-of-type(2){
display: flex;
margin-left:10px;
}
.main .box{
width:50px;
height:100px;
background:pink;
}
</style>
</head>
<body>
<div class="main">
<div class="box">1</div>
<div>
<div class="box">2</div>
<div class="box">3</div>
</div>
</div>
</body>
</html>
方案二
为第一个子元素设置margin-right: auto,让其右外边距自适应剩余空间
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main{
height:200px;
background: skyblue;
display: flex;
align-items: center;
}
.main div{
width:50px;
height:100px;
background:pink;
margin-right:10px;
}
.main div:nth-of-type(1){
margin-right: auto;
}
</style>
</head>
<body>
<div class="main">
<div>1</div>
<div>2</div>
<div>3</div>
</div>
</body>
</html>
这种方案也适用于多组子元素:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main{
height:200px;
background: skyblue;
display: flex;
align-items: center;
}
.main div{
width:50px;
height:100px;
background:pink;
margin-right:10px;
}
.main div:nth-of-type(3){
margin-right: auto;
}
.main div:nth-of-type(6){
margin-right: auto;
}
</style>
</head>
<body>
<div class="main">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<div>9</div>
</div>
</body>
</html>
12-5、等高布局
有时候我们会遇到其中一列要比另一列内容多时,要实现等高的情况:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main{
width:500px;
background:skyblue;
display: flex;
justify-content: space-between;
}
.main div{
width:100px;
background:pink;
}
</style>
</head>
<body>
<div class="main">
<div>
<p>测试内容</p>
<p>测试内容</p>
<p>测试内容</p>
<p>测试内容</p>
</div>
<div>
</div>
</div>
</body>
</html>
- 在不为父元素设置height的情况下,默认会随着子元素内容撑开
- 在不给FlexItem设置高度时,FlexItem在交叉轴上的排列方式默认为stretch,所以就可以轻松实现等高布局
12-6、两列与三列布局
在后台管理系统中,常见的布局方案为左侧菜单栏右侧内容区域自适应或者左右为固定宽度的侧边栏中间内容自适应:
两列布局
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body{
margin:0;
}
.main{
height:100vh;
background:skyblue;
display: flex;
}
.col1{
width:200px;
background: hotpink;
}
.col2{
flex-grow: 1;
background: springgreen;
}
</style>
</head>
<body>
<div class="main">
<div class="col1"></div>
<div class="col2"></div>
</div>
</body>
</html>
- 将父元素设置为flex容器后,左侧FlexItem设置固定宽度,不设置高默认在交叉轴上为stretch,所以会与父容器一样高
- 右侧FlexItem设置flex-grow为1会占满剩余空间,是自适应的
三列布局
同理,为第一列、第三列设置固定宽度,中间flex-grow: 1自适应即可:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body{
margin:0;
}
.main{
height:100vh;
background:skyblue;
display: flex;
}
.col1{
width:200px;
background: hotpink;
}
.col2{
flex-grow: 1;
background: springgreen;
}
.col3 {
width: 100px;
background: seashell;
}
</style>
</head>
<body>
<div class="main">
<div class="col1"></div>
<div class="col2"></div>
<div class="col3"></div>
</div>
</body>
</html>
12-7、粘性页脚布局
有时页脚当内容不满一屏时要展示在最底部,如果内容超出一屏时页脚要随着内容自动适配
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body{
margin:0;
}
.main{
min-height:100vh;
display: flex;
flex-direction: column;
}
.main .header{
height:100px;
background:pink;
}
.main .content{
flex-grow: 1;
}
.main .footer{
height:100px;
background:skyblue;
}
</style>
</head>
<body>
<div class="main">
<div class="header"></div>
<div class="content">
<p>测试内容</p>
<p>测试内容</p>
<p>测试内容</p>
<p>测试内容</p>
<p>测试内容</p>
<p>测试内容</p>
<p>测试内容</p>
<p>测试内容</p>
<p>测试内容</p>
<p>测试内容</p>
<p>测试内容</p>
<p>测试内容</p>
<p>测试内容</p>
<p>测试内容</p>
<p>测试内容</p>
<p>测试内容</p>
<p>测试内容</p>
<p>测试内容</p>
<p>测试内容</p>
<p>测试内容</p>
<p>测试内容</p>
</div>
<div class="footer"></div>
</div>
</body>
</html>
- 将父元素设置为Flex容器,给一个最小高度
- 设置主轴方向为垂直方向,并为中间内容设置flex-grow: 1
- 这样当内容不足一屏时,footer会显示在最底端
- 当内容超出可视区后,footer将自适应中间内容追加到内容底部
12-8、溢出项布局
常见于移动端,比如顶部菜单项比较多,多余的部分在手指进行滑动的时候才去显示出来:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body{
margin:0;
}
.main{
height:100px;
background:skyblue;
display: flex;
align-items: center;
}
.main div{
width:100px;
height:80px;
background:pink;
margin-right:10px;
flex-shrink: 0;
}
</style>
</head>
<body>
<div class="main">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
</div>
</body>
</html>
- 将父元素设置为flex容器后,为子元素添加flex-shrink:0即可,不让子元素去收缩以达到溢出隐藏效果
十三、flex布局常见问题及解决方案
问题描述
有时我们会遇到这样一个问题:
比如想一行展示三条数据,但是服务器返回的数据不足以满足每行都是三条,就会出现以下效果:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
body{
margin:0;
}
.main{
width: 700px;
background:skyblue;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.main div{
width:200px;
height:100px;
background:pink;
margin-bottom: 10px;
}
</style>
</head>
<body>
<div class="main">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
</div>
</body>
</html>
而我们期望的是内容为8 的Flex Item展示在 内容为5的FlexItem下面,但是由于设置的是jusityfy-content: space-between,此时展示在了最右侧,如何解决呢?
解决方案一
不使用jusitify-content,而是通过计算的方式来解决
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main{
width: 700px;
background:skyblue;
display: flex;
flex-wrap: wrap;
/* justify-content: space-between; */
}
.main div{
width:200px;
height:100px;
background:pink;
margin-bottom: 10px;
margin-right: 50px;
}
.main div:nth-child(3n) {
margin-right: 0;
}
</style>
</head>
<body>
<div class="main">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
</div>
</body>
</html>
- 由于Flex容器总宽为700,每一个FlexItem宽度为200,所以余100,那么间隙为50,所以为FlexItem的margin-right设置为50
- 每一行的最后一个FlexItem不需要margin-right
缺点:扩展性不强,如果宽度变了还得重新计算
解决方案二
通过追加列数-2个行内元素(可以用span也可以用i都可以),为其设置一个同FlexItem相同的宽度即可,不需要设置高度
原理:
- 当设置了flex布局后,无论是行内还是块级元素都会参与布局一横行展示
- 当列数为3时,如果这一行只有一个子元素那么展示是没问题的,2个子元素的话会分别展示在最左边与最右边会有问题,所以只需要在所有子元素最后面追加一个span元素且为这个元素设置宽度也为200即可
- 由于不给该span元素设置高度所以不会看见
- 同理,如果该布局需要展示4列,那么需要追加4-2也就是2个span元素即可,因为如果一行展示4个的话,那么这一行有2个或三个元素的话都会有问题,所以至少要追加两个span元素
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.main{
width: 700px;
background:skyblue;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.main div{
width:200px;
height:100px;
background:pink;
margin-bottom: 10px;
}
.main span {
width: 200px;
}
</style>
</head>
<body>
<div class="main">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
<div>8</div>
<span></span>
</div>
</body>
</html>
缺点:会额外多出元素
优点:不需要额外计算,更加有扩展性