# Flex 布局(上)

# 1. 前言/历史

网页布局(layout)是 CSS 的一个重点应用。布局最核心的问题是:如何在一行中显示两个 block 元素?

从历史发展的时间顺序来看,解决这个问题的方案有三种:

  • 使用 <table> 元素布局;

  • 使用 position:static + float 布局;

  • 使用 position:relative + position:absolute 布局。

提示

语义化是 HTML5 的一个重点,例如它提出 <strong> 来替代 <b>。对于常用于布局的无语义的 <div> 元素,HTML5 也提出了几个替代它的语义化元素。

历史上,在经历了上述三种方案的发展之后,W3C 组织最终提出了一个统一的标准的解决方案:Flex 布局。

# 2. Flex 布局是什么?

Flex 是 Flexible Box 的缩写,意为 “弹性布局” ,用来为盒状模型提供最大的灵活性。

任何一个容器都可以指定为 Flex 布局。

.box {
  display: flex;
}

行内元素也可以使用 Flex 布局。

.box {
  display: inline-flex;
}

flexinline-flex 的区别在于,上述例子中的 flex 类型的 .box 是独占一行,而 inline-flex 的 .box 不要求独占一行。

注意

父元素设置为: display: flex 以后,其子元素的 floatclearvertical-align 属性将失效。

# 3. 基本概念

采用 flex 布局的元素,称为 flex 容器(flex container),简称『容器』。它的所有子元素自动成为容器成员,称为 flex 项目(flex item),简称『项目』。

一个 flex 容器中默认存在两根轴:主轴(main axis)和交叉轴(cross axis)。主轴和交叉轴成 90° 垂直交叉,这也是为什么第二根轴被称为『交叉』轴的原因。

flex-01

默认情况下,主轴是水平的,方向是从左往右,那么交叉轴自然就是垂直的。毫无疑问,主轴是水平还是垂直,以及它的方向,都是 可以设置 的 。

注意

主轴的相关属性会影响到交叉轴,但是反之,交叉轴影响不到主轴。

无论水平还是垂直,主轴的开始位置(与边框的交叉点)被称作 主轴的起点(main start)。与之对应的,结束的位置被称作 主轴的终点(main end)。同样,交叉轴也有起点和终点的概念。

  • 主轴水平时:

    • 如果方向是从左往右,那么,毫无疑问,主轴的起点在左边线,终点在右边线。
    • 如果方向是从右往左,那么,毫无疑问,主轴的起点在右边线,终点在左边线。
    • 主轴水平,那么交叉轴必然是垂直。此时,无论主轴是从左往右,还是从右往左,交叉轴的方向都是从上往下 。所以,交叉轴的起点在上边线,终点在下边线。
    • flex-02

主轴垂直时:

  • 如果方向是从上往下,那么,毫无疑问,主轴的起点在上边线,终点在下边线。
  • 如果方向是从下往上,那么,毫无疑问,主轴的起点在下边线,终点在上边线。
  • 主轴垂直,那么交叉轴必然是水平。此时,无论主轴是从上往下,还是从下往上,交叉轴的方向都是从左往右。那么,毫无疑问,交叉轴的起点在左边线,终点在右边线。
  • flex-03

flex 容器的核心属性常见有以下 4 个:

  • flex-direction
  • flex-wrap
  • justify-content
  • align-items

注意

这 4 个属性都是设置在容器元素上的。

# 4. 核心属性:flex-direction

.box {
  flex-direction: row | row-reverse | column | column-reverse;
}

默认值为 row

flex-direction 属性控制着主轴的水平/垂直,以及方向。

  • flex-direction: row

    主轴水平,方向是从左往右。主轴起点在左,终点在右。

    flex-04

    此时,交叉轴自然就是垂直,方向是从上往下。

  • flex-direction: row-reverse

    主轴水平,方向是从右往左。主轴起点在右,终点在左。

    flex-05

    此时,交叉轴自然就是垂直,方向仍然是从上往下。

  • flex-direction: column

    主轴垂直,方向是从上往下。主轴起点在上,终点在下。

    flex-06

    此时,交叉轴自然就是水平,方向是从左往右。

  • flex-direction: column-reverse

    主轴垂直,方向是从下往上。主轴起点在下,终点在上。

    flex-07

    此时,交叉轴自然就是水平,方向仍然是从左往右。

# 5. 核心属性:flex-wrap

默认情况下,flex 元素的子元素(item)都『串』在主轴上(无论主轴是水平还是垂直)

.box {
  flex-wrap: nowrap | wrap | wrap-reverse;
}

flex-wrap 属性定义,如果一条轴线排不下,如何换行。

默认值为 nowrap

  • flex-wrap: nowrap

    nowrap 表示不换行,即,flex 元素的所有子元素,都要在同一行。

    flex-08

  • flex-wrap: wrap

    表示换行,换行的方向是沿交叉轴方向换行。形如:

    flex-09

    • flex-direction: row | row-reverse 时,由于交叉轴的方向是从上往下的。因此,换行时,第二行就在第一行的『下』面。
    • flex-direction: column | column-reverse 时,由于交叉轴方向是从左往右。因此,换行时,第二行就在第一行的『右』边。
  • flex-wrap: wrap-reverse

    表示逆向换行,换上的方向是『逆』交叉轴方向。形如:

    flex-10

    由于这里交叉轴的方向是从上往下,因此,逆向换行时,第二行就在第一行的『上』面。

# 6. 二合一属性:flex-flow 属性

flex-flow 属性不是『新』属性,它是 flex-direction + flex-wrap 属性的组合加简写形式。默认值为 row nowrap

.box {
  flex-flow: <flex-direction> || <flex-wrap>;
}

# 7. 核心属性:justify-content

.box {
  justify-content: flex-start | flex-end | center | space-between | space-around;
}

justify-content 属性定义了项目在『主轴』上的对齐方式。

# 说明
flex-start 默认值,所有 item 整体都靠近主轴的起点位置
flex-end 所有 item 整体靠近主轴的终点位置
center 所有 item 整体居中,主轴的起点和终点有留白
space-between 起点-终点两端对齐,
子元素间均分空白,边线处无间距。
space-around 和 space-between 类似。
不过起点-终点和边框间有间距。
这个间距等于 1/2 的子元素间的间距。

flex-11

注意

这里是以 flex-direction: row 为样本进行的举例。当 flex-direction 属性值为其它值时,相关概念是一样的。

# 8. 核心属性:align-items

.box {
  align-items: flex-start | flex-end | center | baseline | stretch;
}

align-items 属性定义 flex 元素的子元素(item)在交叉轴上如何对齐。

它可能取 5 个值。

# 说明
flex-start 所有子元素都对齐交叉轴的起点。
flex-end 所有子元素都对齐交叉轴的终点。
center 所有子元素都对齐交叉轴中点。
baseline 以所有子元素的第一行文字的基线对齐。
stretch 默认值。
如果子元素自身未设置高度或设为 auto,那么子元素将被拉伸,以便同时对齐交叉轴的起点和终点。

flex-12

注意

这里是以 flex-direction: row 为样本进行的举例。当 flex-direction 属性值为其它值时,相关概念是一样的。

# 9. align-content 属性

align-content 属性和 flex-wrap 属性有点关系。当 flex-wrap: wrapflex-wrap:wrap-reverse 时,如果 Flex 元素的子元素(item)多到一行/一列放不下时,会出现折行的情况。这时,逻辑上就相当于有了多根平行的主轴。

flex-09

align-content 属性就是用来设置这多根平行的主轴在交叉轴方向上的对齐方式。

.box {
  align-content: flex-start | flex-end | center | space-between | space-around | stretch;
}

注意

如果项目只有一根轴线,该属性不起作用。

  • align-content: flex-start

    多根轴线整体靠近主轴的起点:

    align-content-flex-start.png

    如上例,三根主轴轴线紧靠在一起,整体对齐交叉轴的起点位置。

  • align-content: flex-end

    多根轴线整体靠近主轴的终点:

    align-content-flex-end.png

    如上例,三根主轴轴线紧靠在一起,整体对齐交叉轴的终点位置。

  • align-content: flex-center

    多根轴线整体在主轴上居中:

    align-content-center.png

    如上例,三根主轴轴线紧靠在一起,在交叉轴方上居中,即交叉轴的起点和终点位置时留白。

  • align-content: space-between

    多根轴线之间有间距,且最两端的轴线紧挨边线。

    align-content-between.png

    如上例,三根主轴轴线之间有等宽的间隔,同时,第一根轴对齐交叉轴的起点,最后一根轴对齐交叉轴的终点。

    上面这个图例优点小瑕疵,『对齐』表现得不明显。

  • align-content: space-around

    多根轴线之间有间距,但最两端的轴线和边线有间距。

    align-content-around.png

    如上例,三根主轴轴线之间有等宽的间隔,同时,第一根轴和交叉轴的起点有间隔,最后一根轴和交叉轴的终点有间隔,这个『间隔』等于轴与轴之间的间距的 1/2 。

  • align-content: stretch

    align-content-stretch.png

    这是默认值。每根主轴上的元素(item)会被拉伸,逻辑上,各个主轴一起均分交叉轴方向上的空间。前提是主轴上的元素没有指定明确的长度,或者为 auto 。

    不过,各个主轴之间也有间隙,整体呈 主轴-间隙-主轴-间隙-... 的形式。因此,第一根主轴是对齐交叉轴起点位置,而最后一根轴和交叉轴终点位置之间有间隙。