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 是定义方向,横,或者竖,再加上是否“逆向”,就是四种情况:
rowrow-reversecolumncolumn-reverse
这个设置项,本身是很容易理解的。 row 就从左到右, column 就从上到下,加上 -reverse 就反过来。但是,这个设置项会直接影响后面的“条目属性”,及“对齐属性”的意义。我不太想去思考“主轴”,“交叉轴”这类比较正统的概念,暂且,只从 row 去认识 flex 就好了,然后,再单独看看 column 下又是怎样的表现。
3. 条目属性
这块相对来说,就比较简单了,还是 3 + 1 个:
flex-grow: 1flex-shrink: 0flex-basis: autoflex: 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>