CSS 中的 flex 布局
1. 简介
flex 是 css 中新添加的一种布局特性,它的表现,还是像一维的流式布局。但是,因为加了几个控制属性,所以很多时候可以很直接地使用它来代替浮动,来达到想要的效果,并且,它不像浮动会有“清除浮动”的问题。
它的出现,也使得我们在使用“表格布局”,“浮动布局”之外,又有了一个新选择。
机制上来说,可以把 flex 看成是三个部分:
- 容器属性,需要在外层,定义一个节点
display
的值为flex
,这样,它的直接子节点会自动被看成是“flex 条目”。 - 条目属性,“flex 条目”会用的一些控制属性,可以定义在容器中,这些条目的宽度怎么处理,留白怎么处理等。
- 条目对齐,这些属性是作用在“容器”上的,来处理条目与条目之间的相对关系是怎样的。
一个完整的例子是:
<!DOCTYPE html> <html lang="zh-cmn-Hans"> <head> <meta charset="utf-8" /> <title>Flex布局</title> <style type="text/css"> .wrapper { background-color: #ccc; height: 100px; display: flex; flex-direction: row; flex-wrap: wrap; flex-flow: row wrap; justify-content: center; align-items: center; } .wrapper > div { border: 1px solid gray; padding: 10px; box-sizing: border-box; width: 40%; flex-basis: auto; flex-grow: 1; flex-shrink: 0; flex: 1 0 auto; } </style> </head> <body> <div class="wrapper"> <div>A</div> <div>B</div> <div>C</div> </div> </body> </html>
别看属性一列不少,同样的结果,用浮动不一定比它简单,明了的。
上面例子中的 wrapper
就是“容器”,它的三个直接子节点 div
就是“条目”了。
2. 容器属性
先不说对齐,那么容器属性本身还是比较简单的,只有 3 + 1 个:
display: flex;
flex-direction: row;
flex-wrap: wrap;
flex-flow: row wrap;
flex-flow
是 flex-direction
和 flex-wrap
的简写。
display
不用说了。 flex-wrap
表示条目是否换行,默认值是 nowrap
,这种情况下,根据设置宽度要么自动缩小,要么溢出容器。
flex-direction
是定义方向,横,或者竖,再加上是否“逆向”,就是四种情况:
row
row-reverse
column
column-reverse
这个设置项,本身是很容易理解的。 row
就从左到右, column
就从上到下,加上 -reverse
就反过来。但是,这个设置项会直接影响后面的“条目属性”,及“对齐属性”的意义。我不太想去思考“主轴”,“交叉轴”这类比较正统的概念,暂且,只从 row
去认识 flex 就好了,然后,再单独看看 column
下又是怎样的表现。
3. 条目属性
这块相对来说,就比较简单了,还是 3 + 1 个:
flex-grow: 1
flex-shrink: 0
flex-basis: auto
flex: 1 0 auto
flex
是 flex-grow
flex-shrink
flex-basis
的缩写。
这些值定义的行为,主要影响 flex 元素与容器空白的关系。简单来说,如果一个容器宽是 100 ,但是里面所有的元素宽加起来只有 80 ,剩下的 20 如何处理的事。
flex-basis
是“宽度”的意思, auto
会取元素的 width
值,即,如果有值,则是一个定宽,否则,根据里面的内容决定宽度。
flex-grow
和 flex-shrink
,有点像“橡皮长度”的感觉,前者是伸长,后者是缩短。各个无素的这个值,表示它们之间程度的相对值,比如 A 的 flex-grow
是 1 ,而 B 的 flex-grow
是 2 ,那么空白的分配中, B 会比 A 占 得更多。
<!DOCTYPE html> <html lang="zh-cmn-Hans"> <head> <meta charset="utf-8" /> <title>Flex布局</title> <style type="text/css"> .wrapper { background-color: #ccc; padding: 10px; box-sizing: border-box; height: 100px; width: 300px; display: flex; } .wrapper > div { border: 1px solid black; background-color: red; width: 50px; flex: 1 0 auto; } </style> </head> <body> <div class="wrapper"> <div>A</div> <div>B</div> <div>C</div> </div> </body> </html>
上的例子中,三个子元素会填充满 wrapper
,像是浮动中的 width: 33.33%
的效果一样,而且,高度会自动填充为容器的高。
flex
跟容器的 flex-wrap
一起,会有不一样的作用:
<!DOCTYPE html> <html lang="zh-cmn-Hans"> <head> <meta charset="utf-8" /> <title>Flex布局</title> <style type="text/css"> .wrapper { background-color: #ccc; padding: 10px; box-sizing: content-box; height: 100px; width: 300px; display: flex; flex-wrap: wrap; } .wrapper > div { border: 1px solid black; background-color: red; width: 150px; flex: 1 0 auto; } </style> </head> <body> <div class="wrapper"> <div>A</div> <div>B</div> <div>C</div> </div> </body> </html>
上面的例子,因为设置了“可换行”,并且子元素“可伸长”,在容器高度一定,一行放不下两个子元素的情况下, A,B,C 三块,会变成从上往下排列了。
如果把子元素的 width: 150px
改成 width: 120
,结果就是 A,B 在同一行,每个宽 150 , C 独立一行,宽 300 。记住,宽度变成是因为 flex-grow
作用。如果想要简单的 float: left
那样的效果,只需要 flex-grow: 0
就好了。
4. 条目分布与对齐
水平分布,是在容器中使用 justify-content
属性控制,在子元素没有伸缩的情况下,子元素在足够大的容器中如何排列的行为,简单列几个取值是:
flex-start
靠左flex-end
靠右center
整体居中space-around
均匀分布space-between
两边贴边的均匀分布
垂直对齐方面,是在容器中使用 align-items
来控制,这个属性与子元素的 height
有关系,常见的几个取值:
stretch
,在子元素没有定高情况下,这个默认选项,会使子元素自动撑满高度。flex-start
,顶部对齐。flex-end
,底部对齐。center
,居中。baseline
,靠上的基线对齐。(内容字体大小不一的情况下,才能看出与flex-start
的区别)
例子:
<!DOCTYPE html> <html lang="zh-cmn-Hans"> <head> <meta charset="utf-8" /> <title>Flex布局</title> <style type="text/css"> .wrapper { background-color: #ccc; padding: 10px; box-sizing: content-box; height: 100px; width: 800px; display: flex; flex-wrap: nowrap; justify-content: space-between; align-items: flex-start; } .wrapper > div { border: 1px solid black; background-color: red; width: 120px; height: auto; flex: 0 0 auto; } </style> </head> <body> <div class="wrapper"> <div>A</div> <div><span style="font-size: 50px;">B</span></div> <div>C</div> </div> </body> </html>
5. column
当把容器的 flex-direction
默认值 row
改成 column
之后,很多属性的行为都会不同了。
我们之前说的什么“水平居中”,“左对齐”之类的说法,其实在 column
场景下,是对不上的。
row
的子元素排列,是从左往右排,沿水平轴。 column
的排列,是从上往下排,沿垂直轴。对应地,很多之前介绍的属性的行为,跟子元素排列的轴是一致的,所以 row
的时候我们说的“水平”,在 column
下就变成“垂直”了。。
flex-grow
和 flex-start
也是同样的道理, row
下它们控制宽, column
就变成控制高了。
示例:
<!DOCTYPE html> <html lang="zh-cmn-Hans"> <head> <meta charset="utf-8" /> <title>Flex布局</title> <style type="text/css"> .wrapper { background-color: #ccc; padding: 10px; box-sizing: content-box; height: 100px; width: 800px; display: flex; flex-direction: column; flex-wrap: wrap; justify-content: flex-start; align-items: center } .wrapper > div { border: 1px solid black; background-color: red; width: 120px; height: 30px; flex: 1 0 auto; } </style> </head> <body> <div class="wrapper"> <div>A</div> <div style="width: 200px; height: 50px;"><span style="font-size: 50px;">B</span></div> <div>C</div> </div> </body> </html>
column
的 flex
,有点像排版中的“分栏”,之前, css 应该是直接做不出来的吧。
6. 常用布局
6.1. 左右
<div style="display: flex; height: 300px;"> <div style="flex: 0 0 200px; background-color: lightblue;"></div> <div style="flex: 1 0 auto; background-color: yellow;"></div> </div>
6.2. 右左
<div style="display: flex; height: 300px;"> <div style="flex: 0 0 200px; background-color: lightblue;"></div> <div style="flex: 1 0 auto; background-color: yellow;"></div> </div>
6.3. 左中右
<div style="display: flex; height: 300px;"> <div style="flex: 0 0 200px; background-color: lightblue;"></div> <div style="flex: 1 0 auto; background-color: yellow;"></div> <div style="flex: 0 0 200px; background-color: lightblue;"></div> </div>
6.4. 从左到右
<div style="display: flex; height: auto; flex-wrap: wrap;"> <div style="flex: 0 0 100px; background-color: lightblue; height: 100px; margin: 10px;"></div> <div style="flex: 0 0 100px; background-color: lightblue; height: 100px; margin: 10px;"></div> <div style="flex: 0 0 100px; background-color: lightblue; height: 100px; margin: 10px;"></div> <div style="flex: 0 0 100px; background-color: lightblue; height: 100px; margin: 10px;"></div> <div style="flex: 0 0 100px; background-color: lightblue; height: 100px; margin: 10px;"></div> <div style="flex: 0 0 100px; background-color: lightblue; height: 100px; margin: 10px;"></div> <div style="flex: 0 0 100px; background-color: lightblue; height: 100px; margin: 10px;"></div> <div style="flex: 0 0 100px; background-color: lightblue; height: 100px; margin: 10px;"></div> <div style="flex: 0 0 100px; background-color: lightblue; height: 100px; margin: 10px;"></div> </div>
6.5. 均匀分布(不贴边)
<div style="display: flex; height: auto; justify-content: space-around;"> <div style="flex: 0 0 100px; background-color: lightblue; height: 100px;"></div> <div style="flex: 0 0 100px; background-color: lightblue; height: 100px;"></div> <div style="flex: 0 0 100px; background-color: lightblue; height: 100px;"></div> </div>
6.6. 均匀分布(贴边)
<div style="display: flex; height: auto; justify-content: space-around;"> <div style="flex: 0 0 100px; background-color: lightblue; height: 100px;"></div> <div style="flex: 0 0 100px; background-color: lightblue; height: 100px;"></div> <div style="flex: 0 0 100px; background-color: lightblue; height: 100px;"></div> </div>
6.7. 分栏
<div style="display: flex; height: 300px; flex-flow: column wrap"> <div style="flex: 0 0 100px; background-color: lightblue; width: auto; margin: 10px;">A</div> <div style="flex: 0 0 100px; background-color: lightblue; width: auto; margin: 10px;">B</div> <div style="flex: 0 0 100px; background-color: lightblue; width: auto; margin: 10px;">C</div> </div>