Serenader

Learning by sharing.

CSS3 中的 Flexbox 详解

什么是 Flexbox ?

Flexbox 是指 CSS 中将元素的 display 规则设置为 flex 或者 inline-flex 时所展示出来的布局模式。

主要为了解决什么问题?

  1. 高效灵活地布局,对齐。
    在 Flexbox 尚未出现之前,页面的布局通常都是用浮动或者绝对定位和相对定位来实现。布局方式不多,但是也勉强可用。但是到后来,随着前端技术的快速发展以及页面布局需求的快速迭代,传统的布局方式在越来越酷炫和高大上的设计稿前面,就显得表现力不足以及布局变得麻烦而且不够灵活。特别是对于元素的对齐,往往得花费一定的精力使用各种技巧才能实现。Flexbox 这一布局方式的出现则可以看做是为了解决布局和对齐这个问题的一种尝试。

  2. 解决移动端上页面布局的难题。
    如今的智能设备越来越多,设备的尺寸也越来越多。很明显,开发者不可能为每一种屏幕尺寸提供一套解决方案,只能说提供一套通用的解决方案来适配各种各样的设备。再者,现在的智能设备大多都支持屏幕旋转功能。而屏幕旋转功能则会在一定程度上影响到页面的布局。这个问题也是传统布局方式所无能为力的,或者说,目前为止仍然没有一种非常优雅地解决方案来解决这个问题,直到 Flexbox 的出现。

Flexbox 使用方法

假设我们有如下的 HTML 代码片段:

<div class="flexbox-parent">
    <div class="flexbox-children child-1">1</div>
    <div class="flexbox-children child-2">2</div>
    <div class="flexbox-children child-3">3</div>
</div>
.flexbox-parent {
    border: 1px solid #ccc;
    display: flex;
    width: 300px;
    margin: 20px auto;
}
.flexbox-children {
    background: #f5f7f8;
    line-height: 50px;
    height: 50px;
    text-align: center;
    border: 1px solid #eee;
}
.child-1 {
    width: 50px;
}
.child-2 {
    width: 80px;
}
.child-3 {
    width: 100px;
}

1
2
3

对于 flexbox-parent

display

当为父元素设置 display:flex 或者 display:inline-flex 时,则会在父元素和子元素中产生一个 Flexbox 上下文。

Flexbox 可以有两种类型,一种是 block ,一种是 inline-block。

.flexbox-parent {
	display: flex; /* or inline-flex */
}
flex-direction

与传统文档流不同的是,Flexbox 可以设置子元素的排列方式,可以是竖向排列,也可以是横向排列。但是一个 Flexbox 只能设置一种排列方式。

.flexbox-parent {
	flex-direction: row(default value) | row-reverse | column | column-reverse;
}

flex-direction: row

1
2
3

flex-direction: row-reverse

1
2
3

flex-direction: column

1
2
3

flex-direction: column-reverse

1
2
3

flex-direction:rowflex-direction: row-reverse 则是横向排列,其中 row-reverse 则是从右到左排列。

flex-wrap

white-space: nowrap 类似,对于 flexbox-parent 而言,也可以设置当元素长度超过父元素宽度时是否需要折行的行为:

.flexbox-parent {
	flex-wrap: nowrap (defalut value) | wrap | wrap-reverse;
}

flex-wrap: nowrap

1
2
3

flex-wrap:wrap

1
2
3

flex-wrap:wrap-reverse

1
2
3

默认行为是不换行,即永远保持一行。flex-wrap: wrap 则可以让子元素自动换行,而且方向与默认方向一致,即从左到右。而 flex-wrap: wrap-reverse 则是换行之后方向与默认方向相反。可以看到,Flexbox 对子元素的方向控制非常精准。


flex-directionflex-wrap 这两条规则可以缩写成一条规则:

.flexbox-parent {
	flex-flow: <flex-direction>  <flex-wrap>;
}

flex-flow 的默认值是 row nowrap

justify-content

另外一个与传统布局不同的是,Flexbox 可以非常轻松地控制子元素之间的水平间距:

.flexbox-parent {
	justify-content: flex-start (default value) | flex-end | center | space-between | space-around;
}

justify-content:flex-start

1
2
3

justify-content: flex-end

1
2
3

justify-content: center

1
2
3

justify-content: space-between

1
2
3

justify-content: space-around

1
2
3
  • flex-start

    Flexbox 的第一个子元素会紧贴父元素的左边,然后后面的子元素紧跟在右边。当所有子元素的总宽度小于父元素的宽度时,则会在最右边产生空白。这是默认的表现方式

  • flex-end

    flex-start 相反,它是最后一个子元素紧贴父元素的右边,然后前面的子元素紧跟在左边。

  • center

    第一个子元素和最后一个子元素均不贴紧父元素的两边,而是把所有的子元素紧贴然后水平居中,两边留空白。

  • space-between

    flex-startflex-end 的一个融合,但是又不完全是他们的融合。这种模式下,第一个子元素会紧贴父元素的左边,最后一个子元素会紧贴父元素的右边,然后中间的子元素之间的空白均等。

  • space-around

    space-between 类似,只是第一个元素和最后一个元素并不紧贴在父元素两边,而是均等两边的空白。

align-items

Flexbox 除了可以控制子元素水平方向的间距,还可以在垂直方向上控制子元素的对齐。

.flexbox-parent {
	align-items: flex-start | flex-end | center | stretch (default value) | baseline;
}

align-items: flex-start

1
2
3

align-items: flex-end

1
2
3

align-items: center

1
2
3

align-items: stretch

1
2
3

align-items: baseline

1
2
3
  • flex-start

    将会使得子元素在垂直方向上以父元素的顶部对齐。

  • flex-end

    则相反,子元素在垂直方向上以父元素的底部对齐。

  • center

    顾名思义,就是在垂直方向上居中显示。

  • stretch

    会将子元素的高度等同于父元素的高度。这是默认显示方式。

  • baseline

    则会以子元素里面的文字基线作为基准进行垂直方向的对齐。

align-content

justify-content 类似,当父元素里面的子元素有多行时,行与行之间的间隔可以使用 align-content 来控制:

.flexbox-parent {
	align-content: flex-start | flex-end | center | stretch (default value) | space-between | space-around;
}

具体属性效果参考 justify-content

对于 flexbox-children

order

默认情况下,子元素的排列是由代码的顺序决定的。但是,对于 Flexbox 的子元素而言,可以通过添加 order 规则来改变子元素的排列位置。

.flexbox-children {
	order: <interger>;
}
1
2
3
1
2
3
当设置了 `order` 属性之后,子元素之间的位置则通过 `order` 的值进行排列,而不再是根据代码的顺序决定。
flex-grow

当 Flexbox 里面的子元素的总宽度不能填满父元素的宽度时,可以通过 flex-grow 来设置是否自动增加宽度来填满空白。

.flexbox-children {
	flex-grow: <number>; /* default 0 */
}
1
2
3
1
2
3

flex-grow 接受数字,但是负数无效。当子元素均设置为 1 时,将会把水平方向的空白平分成几份,然后填充到子元素的宽度里面,以此来填充空白。而当有某个元素设置为 2 时,则此子元素会分配到 2 倍空白。

flex-shrink

flex-shrink 规则可以用来放大或者缩小子元素本身。

.flexbox-children {
	flex-shrink: <number>; /* default 1 */
}
1
2
3
1
2
3
1
2
3
`flex-shrink` 接受数字,但是负数无效。
flex-basis

flex-basis: <length> | auto 这个规则由于浏览器支持不是很好,所以不说。


以上讲到的几个 Flexbox 子元素的规则除了 order 之外都可以使用一个简写的规则来表示:

.flexbox-children {
	flex: none |  [ <flex-grow> <flex-shrink> ? || <flex-basis>]
}

flex 第一个参数是 flex-grow ,第二个参数和第三个参数是可选的。默认值是 0 1 auto 。推荐使用 flex 这个规则来设置 flex-growflex-shrinkflex-basis 这三个规则。

align-self

上面的 flexbox-parent 部分讲到了可以使用 align-items 来对 Flexbox 的子元素进行垂直方向的对其,然而在 flexbox-children 中,可以使用 align-self 来覆盖默认的垂直对其方式。

.flexbox-children {
	align-self: auto | flex-start | flex-end | center | baseline | stretch;
}

align-items: flex-start

1
2
3

具体属性值解释参考 align-items

需要注意的地方

floatclearvertical-align 规则对于 Flex 元素不生效。

使用案例

完美居中

.flexbox-parent {
    display: flex;
    height: 300px;
    align-items: center;
    justify-content: center;
}
.flexbox-children {
    width: 100px;
    height: 100px;
}
1

自适应两栏布局

.flex-27 {
    height: 300px;
    width: 60%;
}
.flex-27 .left {
    width: 100px;
    height:100%
}
.flex-27 .right {
    flex-grow: 1;
    height:100%;
    background: none;
}
This is sidebar
This is main content

导航栏带右侧按钮

.flex-28 {
    border-radius: 3px;
    padding:10px 5px;
}
.flex-28 a {
    padding: 0 10px;
}
.flex-28 .left {
    flex-grow: 1
}

各浏览器实现的 Flexbox 目前存在的 bug

很遗憾的是,尽管现在已经有很多浏览器支持 Flexbox 了,但是,仍旧有许多 bugs 存在。具体的问题可以在 https://github.com/philipwalton/flexbugs 上找到。

浏览器兼容

Flexbox 目前存在三个版本:

  • (new)最新的版本,语法就是上面提到的: display: flex
  • (tweener) 2011 年开始有的非官方语法:display: flexbox
  • (old) 2009 年开始有的语法: display: box
Chrome Safari Firefox Opera IE Android iOS
20-
(old)
3.1+
(old)
2-21
(old)
12.1+
(new)
10
(tweener)
2.1+
(old)
3.2+
(old)
21+
(new)
6.1+
(new)
22+
(new)
11+
(new)
4.4+
(new)
7.1+
(new)

Reference

Comments is loading...

Comments is loading...