一个完整的Flexbox指南

发布于 大漠

Flexbox布局(Flexible Box)模块(目前处于W3C工作草案)旨在提供一个更佳有效的布局方式,更好的控制项目的对齐和自由分配容器空间,即使它们的大小是未知的或动态的。因此得其名"flex"。

W3C兴趣小组,翻译了一份中文,感兴趣的可以点击这里

Flex布局背后的主要思想是给容器控制项目(Flex项目)的宽度、高度的能力,使用Flex项目可以自动填满容器的可用空间(主要是适应所有类型的显示设备和屏幕大小)。Flex容器使用Flex项目可以自动放大与收缩,用来填补可用的空闲空间。

最重要的是Flexbox布局的direction-agnostic与常规则布局(块的vertically-based和内联horizontally-based)不同。而那些好的页面缺乏灵活性,特别是方向的改变,大小的调整,伸展,收缩等等。

**注:**Flexbox布局比较适合Web应用程序的一些小组件和小规模的布局,而Grid布局更适合用于一些大规模的布局。

有关于Grid布局模块的中文资料可以点击这里查阅。

基础知识和专业术语

因为Flexbox是一个模块,而不是一个单一的属性,他涉及很多东西。其中有一些是容器上的属性(父元素,也被称为“flex容器”),而有一些是容器子元素上的属性(常称为“flex项目”)。

如果常规布局是基于块和内联文本流方向,那么Flex布局就是基于“Flex-flow”方向。请看看下面来自规范中的一张图,其解释了Flex布局的主要思想。

Flexbox

基本上,Flex项目是沿着main axis(从main-startmain-end)或者cross axis(从cross-startcross-end)排列。

  • **main axis:**Flex容器的主轴主要用来配置Flex项目。注意,它不一定是水平,这主要取决于flex-direction属性。
  • **main-start | main-end:**Flex项目的配置从容器的主轴起点边开始,往主轴终点边结束。
  • **main size:**Flex项目的在主轴方向的宽度或高度就是项目的主轴长度,Flex项目的主轴长度属性是widthheight属性,由哪一个对着主轴方向决定。
  • **cross axis:**与主轴垂直的轴称作侧轴,是侧轴方向的延伸。
  • **cross-start | cross-end:**伸缩行的配置从容器的侧轴起点边开始,往侧轴终点边结束。
  • **cross size:**Flex项目的在侧轴方向的宽度或高度就是项目的侧轴长度,Flex项目的侧轴长度属性是widthheight属性,由哪一个对着侧轴方向决定。

##Flex容器属性

container

###display

定义一个Flex容器,根据其取的值来决定是内联还是块。Flex容器会为其内容建立新的伸缩格式化上下文。

.container {
  display: flex; /* or inline-flex */
}

Flex容器不是块容器,因此有些设计用来控制块布局的属片在Flexbox布局中不适用。特别是:多列组中所有column-*属性、floatclear属性和vertical-align属性在Flex容器上没有作用。


如果元素display的值指定为inline-flex,而且元素是一个浮动元素或绝对定位元素,则display的计算值是flex

开启Flex容器:让一个元素变成伸缩容器

规范版本 属性名称 块伸缩容器 内联伸缩容器
标准版本 display flex inline-flex
混合版本 display flexbox inline-flexbox
最老版本 display box inline-box

flex-direction

flex-direction

这是用来创建方轴,从而定义Flex项目在Flex容器中放置的方向。Flexbox是一种单方向的布局概念。认为Flex项目主要排列方式要么是水平排列,要么是垂直列排列。

.container {
  flex-direction: row | row-reverse | column | column-reverse;
}
  • **row(默认值):**如果书写方式是ltr,那么Flex项目从左向右排列;如果书写方式是rtl,那么Flex项目从右向左排列
  • **row-reverse:**如果书写方式是ltr,那么Flex项目从右向左排列;如果书写方式是rtl,那么Flex项目从左向右排列
  • **column:**和row类似,只不过方向是从上到下排列
  • **column-reverse:**和row-reverse类似,只不过方向是从下向上排列

伸缩流:指定伸缩容器主轴的伸缩流方向

规范版本 属性名称 水平方向 反向水平 垂直方向 反向垂直
标准版本 flex-direction row row-reverse column column-reverse
混合版本 flex-direction row row-reverse column column-reverse
最老版本 box-orient
box-direction
horizontal
normal
horizontal
reverse
vertical
normal
vertical
reverse

flex-wrap

flex-wrap

默认情况之下,Flex项目都尽可能在一行显示。你可以根据flex-wrap的属性值来改变,让Flex项目多行显示。方向在这也扮演了一个重要角度,决定新的一行堆放方向。

.container{
  flex-wrap: nowrap | wrap | wrap-reverse;
}
  • **nowrap(默认值):**单行显示,如果书写方式是ltr,Flex项目从左向右排列,反之rtl,从右向左排列
  • **wrap:**多行显示,如果书写方式是ltr,Flex项目从左向右排列,反之rtl,从右向左排列
  • **wrap-reverse:**多行显示,如果书写方式是ltr,Flex项目从右向左排列,反之rtl,从左向右排列

换行:指定伸缩项目是否沿着侧轴排列

规范版本 属性名称 不换行 换行 反转换行
标准版本 flex-wrap nowrap wrap wrap-reverse
混合版本 flex-wrap nowrap wrap wrap-reverse
最老版本 box-lines single multiple N/A

flex-flow(适用于flex容器元素)

这是flex-directionflex-wrap两个属性的缩写。两个属性决定了伸缩容器的主轴与侧轴。默认值是row nowrap(中间用空格隔开)。

flex-flow: <‘flex-direction’> || <‘flex-wrap’>

justify-content

justify-content

用于在主轴上对齐伸缩项目。这一行为会在所有可伸缩长度及所有自动边距均被解释后进行。当一行上的所有伸缩项目都不能伸缩或可伸缩但是已经达到其最大长度时,这一属性才会对多余的空间进行分配。当项目溢出某一行时,这一属性也会在项目的对齐上施加一些控制。

.container {
  justify-content: flex-start | flex-end | center | space-between | space-around;
}
  • **flex-start(默认值):**伸缩项目向一行的起始位置靠齐。该行的第一个伸缩项目在主轴起点边的外边距与该行在主轴起点的边对齐,同时所有后续的伸缩项目与其前一个项目对齐。
  • **flex-end:**伸缩项目向一行的结束位置靠齐。该行的最后一个伸缩项目在主轴终点边的外边距与该行在主轴终点的边对齐,同时所有前面的伸缩项目与其后一个项目对齐。
  • **center:**伸缩项目向一行的中间位置靠齐。该行的伸缩项目将相互对齐并在行中居中对齐,同时第一个项目与该行在主轴起点的边的距离等同与最后一个项目与该行在主轴终点的边的距离(如果剩余空间是负数,则保持两端溢出的长度相等)。
  • **space-between:**伸缩项目会平均地分布在行里。如果剩余空间是负数,或该行只有一个伸缩项目,则此值等效于flex-start。在其它情况下,第一个项目在主轴起点边的外边距会与该行在主轴起点的边对齐,同时最后一个项目在主轴终点边的外边距与该行在主轴终点的边对齐,而剩下的伸缩项目在确保两两之间的空白空间相等下平均分布。
  • **space-around:**伸缩项目会平均地分布在行里,两端保留一半的空间。如果剩余空间是负数,或该行只有一个伸缩项目,则该值等效于center。在其它情况下,伸缩项目在确保两两之间的空白空间相等,同时第一个元素前的空间以及最后一个元素后的空间为其他空白空间的一半下平均分布。

主轴对齐方式:指定伸缩项目沿主轴对齐方式

规范版本 属性名称 start center end justify distribute
标准版本 justify-content flex-start center flex-end space-between space-around
混合版本 flex-pack start center end justify distribute
最老版本 box-pack start center end justify N/A

align-items

align-items

伸缩项目可以在伸缩容器的当前行的侧轴上进行对齐,这类似于justify-content属性,但是是另一个方向。align-items可以用来设置伸缩容器中包括匿名伸缩项目的所有项目的对齐方式。

.container {
  align-items: flex-start | flex-end | center | baseline | stretch;
}
  • **flex-start:**伸缩项目在侧轴起点边的外边距紧靠住该行在侧轴起始的边。
  • **flex-end:**伸缩项目在侧轴终点边的外边距靠住该行在侧轴终点的边 。
  • **center:**伸缩项目的外边距盒在该行的侧轴上居中放置。(如果伸缩行的尺寸小于伸缩项目,则伸缩项目会向两个方向溢出相同的量)。
  • **baseline:**如果伸缩项目的行内轴与侧轴为同一条,则该值和flex-start等效。其它情况下,该值将参与基线对齐。所有参与该对齐方式的伸缩项目将按下列方式排列:首先将这些伸缩项目的基线进行对齐,随后其中基线至侧轴起点边的外边距距离最长的那个项目将紧靠住该行在侧轴起点的边。
  • **stretch:**如果侧轴长度属性的值为auto,则此值会使项目的外边距盒的尺寸在遵照min/max-width/height属性的限制下尽可能接近所在行的尺寸。

侧轴对齐方式:指定伸缩项目沿着侧轴对齐方式

规范版本 属性名称 start center end baseline stretch
标准版本 align-items flex-start center flex-end baseline stretch
混合版本 flex-align start center end baseline stretch
最老版本 box-align start center end baseline stretch

align-content

align-content

当伸缩容器的侧轴还有多余空间时,align-content属性可以用来调准伸缩行在伸缩容器里的对齐方式,这与调准伸缩项目在主轴上对齐方式的justify-content属性类似。

请注意本属性在只有一行的伸缩容器上没有效果。

.container {
  align-content: flex-start | flex-end | center | space-between | space-around | stretch;
}
  • **flex-start:**各行向伸缩容器的起点位置堆叠。伸缩容器中第一行在侧轴起点的边会紧靠住伸缩容器在侧轴起点的边,之后的每一行都紧靠住前面一行。
  • **flex-end:**各行向伸缩容器的结束位置堆叠。伸缩容器中最后一行在侧轴终点的边会紧靠住该伸缩容器在侧轴终点的边,之前的每一行都紧靠住后面一行。
  • **center:**各行向伸缩容器的中间位置堆叠。各行两两紧靠住同时在伸缩容器中居中对齐,保持伸缩容器在侧轴起点边的内容边和第一行之间的距离与该容器在侧轴终点边的内容边与第最后一行之间的距离相等。(如果剩下的空间是负数,则行的堆叠会向两个方向溢出的相等距离。)
  • **space-between:**各行在伸缩容器中平均分布。如果剩余的空间是负数或伸缩容器中只有一行,该值等效于flex-start。在其它情况下,第一行在侧轴起点的边会紧靠住伸缩容器在侧轴起点边的内容边,最后一行在侧轴终点的边会紧靠住伸缩容器在侧轴终点的内容边,剩余的行在保持两两之间的空间相等的状况下排列。
  • **space-around:**各行在伸缩容器中平均分布,在两边各有一半的空间。如果剩余的空间是负数或伸缩容器中只有一行,该值等效于center。在其它情况下,各行会在保持两两之间的空间相等,同时第一行前面及最后一行后面的空间是其他空间的一半的状况下排列。
  • **stretch:**各行将会伸展以占用剩余的空间。如果剩余的空间是负数,该值等效于flex-start。在其它情况下,剩余空间被所有行平分,扩大各行的侧轴尺寸。

**注意:**只有多行的伸缩容器才会在侧轴上有多余的空间以供对齐,因为仅包含一行的伸缩容器中,唯一的一行会自动伸展填充全部的空间。

伸缩项目行对齐方式:指定伸缩项目行在侧轴的对齐方式

规范版本 属性名称 start center end justify distribute stretch
标准版本 align-content flex-start center flex-end space-between space-around stretch
混合版本 flex-line-pack start center end justify distribute stretch
最老版本 N/A N/A N/A N/A N/A N/A N/A

##Flex项目属性

Flex项目

order

order

默认情况,Flex项目是按文档源的流顺序排列。然而,在Flex容器中可以通过order属性来控制Flex项目的顺序源。

.item {
  order: <integer>;
}

根据order重新排序伸缩项目。有最小(负值最大)order的伸缩项目排在第一个。若有多个项目有相同的order值,这些项目照文件顺序排。这个步骤影响了伸缩项目生盒树成的盒子的顺序,也影响了后面的演算法如何处理各项目。

显示顺序:指定伸缩项目的顺序

规范版本 属性名称 属性值
标准版本 order <number>
混合版本 flex-order <number>
最老版本 flex-order <integer>

flex-grow

flex-grow

如果有必要的话,flex-grow可以定义一个Flex项目的扩大比例。它接受一个没有单位的值作为一个比例。它可以使用Flex项目完全占用Flex容器可用的空间。

如果所有Flex项目的flex-grow设置为1时,表示Flex容器中的Flex项目具有相等的尺寸。如果你给其中一个Flex项目设置flex-grow的值为2,那么这个Flex项目的尺寸将是其他Flex项目两倍(其他Flex项目的flex-grow值为1)。

.item {
  flex-grow: <number>; /* default 0 */
}

注意:flex-grow取负值将失效。

flex-shrink

如果有必要,flex-shrink可以定义Flex项目的缩小比例。

.item {
  flex-shrink: <number>; /* default 1 */
}

注意:flex-shrink取负值将失效。

flex-basis

flex-basis属性定义了Flex项目在分配Flex容器剩余空间之前的一个默认尺寸。main-size值使它具有匹配的宽度或高度,不过都需要取决于flex-direction的值。

.item {
  flex-basis: <length> | auto; /* default auto */
}

如果设置为0,内容不在考虑周围额外空间。如果设置为auto,额外空间会基于flex-grow值做分布。如下图所示:

flex-basis

flex

flexflex-growflex-shrinkflex-basis三个属性的缩写。第二个和第三个参数(flex-shrinkflex-basis)是可选值。其默认值是0 1 auto

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

建议您 使用此简写属性,而不是设置单独属性。注意,如果flex取值为none时,其相当于取值为0 0 auto

请注意flex-growflex-basis的初始值与他们在flex缩写被省略时的 默认值不同。这里的设计是为了让flex缩写在最常见的情景下比较好用。

flex常见值

flex: 0 auto,flex: initialflex: 0 1 auto相同。(这也就是初始值。)根据widthheight属性决定元素的尺寸。(如果项目的主轴长度属性的计算值为auto,则会根据其内容来决定元素尺寸。)当剩余空间为正值时,伸缩项目无法伸缩,但当空间不足时,伸缩项目可收缩至其最小值。网页作者可以用对齐相关的属性以及margin属性的auto值控制伸缩项目沿着主轴的对齐方式。

flex: autoflex: 1 1 auto相同。根据widthheight属性决定元素的尺寸,但是完全可以伸缩,会吸收主轴上剩下的空间。如果所有项目均为flex: autoflex: initialflex: none,则在项目尺寸决定后,剩余的正空间会被平分给是flex: auto的项目。

flex: noneflex: 0 0 auto相同。根据widthheight属性决定元素的尺寸,但是完全不可伸缩。其效果与initial类似,但即使在空间不够而溢出的情况下,伸缩项目也不能收缩。

flex: <positive-number>flex: 1 0px相同。该值使元素可伸缩,并将伸缩基准值设置为零,导致该项目会根据设置的比率占用伸缩容器的剩余空间。如果一个伸缩容器里的所有项目都使用此模式,则它们的尺寸会正比于指定的伸缩比率。

默认状态下,伸缩项目不会收缩至比其最小内容尺寸(最长的英文词或是固定尺寸元素的长度)更小。网页作者可以靠设置min-widthmin-height属性来改变这个默认状态。(参见《伸缩项目的默认最小长度》。)

本规范鼓励网页作者使用flex缩写来控制可伸缩性,而不是使用单独的属性,因为flex缩写会在最常见的情景正确的重置未指定的部件值。

伸缩性:指定伸缩项目如何伸缩尺寸

规范版本 属性名称 属性值
标准版本 flex none | [<flex-grow> <flex-shrink> ?|| <flex-basis>]
混合版本 flex none | [[<pos-flex> <neg-flex> ?]|| <preferred-size>]
最老版本 box-flex <number>

align-self

align-self

align-self则用来在单独的伸缩项目上覆写默认的对齐方式。(对于匿名伸缩项目,align-self的值永远与其关联的伸缩容器的align-items的值相同。)

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

若伸缩项目的任一个侧轴上的外边距为auto,则align-self没有效果。

如果align-self的值为auto,则其计算值为元素的父元素的align-items值,如果该元素没有父元素,则计算值为stretch。对齐属性值的定义如下:

  • **flex-start:**伸缩项目在侧轴起点边的外边距紧靠住该行在侧轴起始的边。
  • **flex-end:**伸缩项目在侧轴终点边的外边距靠住该行在侧轴终点的边 。
  • **center:**伸缩项目的外边距盒在该行的侧轴上居中放置。(如果伸缩行的尺寸小于伸缩项目,则伸缩项目会向两个方向溢出相同的量)。
  • **baseline:**如果伸缩项目的行内轴与侧轴为同一条,则该值和flex-start等效。其它情况下,该值将参与基线对齐。所有参与该对齐方式的伸缩项目将按下列方式排列:首先将这些伸缩项目的基线进行对齐,随后其中基线至侧轴起点边的外边距距离最长的那个项目将紧靠住该行在侧轴起点的边。
  • **stretch:**如果侧轴长度属性的值为auto,则此值会使项目的外边距盒的尺寸在遵照min/max-width/height属性的限制下尽可能接近所在行的尺寸。

注意:如果伸缩伸缩的高度有限制,此可能导致伸缩项目的内容溢出该项目。

单个伸缩项目侧轴对齐方式

规范版本 属性名称 auto start center end baseline stretch
标准版本 align-self auto flex-start center flex-end baseline stretch
混合版本 flex-item-align auto start center end baseline stretch
最老版本 N/A N/A N/A N/A N/A N/A N/A

注意:floatclearvertical-align属性在Flex项目中无效

##示例

让我们从一个非常简单的例子开始,解决一个问题:完美的居中。如果使用Flexbox之后,没有一个方法比这个更简单。

.parent {
  display: flex;
  height: 300px; /* Or whatever */
}

.child {
  width: 100px;  /* Or whatever */
  height: 100px; /* Or whatever */
  margin: auto;  /* Magic! */
}

这主要依赖于margin设置auto,自动分配Flex容器额外空间。这使用Flex项目在水平和垂直方向都居中。

现在让我们使用更多的属性。有六个固定尺寸的列表,但它们可能是auto-sized。我们希望他们能均匀和很好地分布在容器的不平轴,以便我们调整浏览器的时候,一切都很好(在没有使用媒体查询之下)。

.flex-container {
  /* We first create a flex layout context */
  display: flex;
  
  /* Then we define the flow direction and if we allow the items to wrap 
   - Remember this is the same as:
   - flex-direction: row;
   - flex-wrap: wrap;
   */
  flex-flow: row wrap;
  
  /* Then we define how is distributed the remaining space */
  justify-content: space-around;
}

完成了。其他一切都不过是一些样式问题。下面是在Codepen上创建的一个示例,你可以在Codepen上打开这个示例,并调整你浏览窗口大小,看看会发生些什么。

让我们再试试别的东西。想象一下,我们站点右侧顶端有一个导航。但我们希望它在medium-sized屏幕和小型设备上能单列显示。这样的效果也很容易。

/* Large */
.navigation {
  display: flex;
  flex-flow: row wrap;
  /* This aligns items to the end line on main-axis */
  justify-content: flex-end;
}

/* Medium screens */
@media all and (max-width: 800px) {
  .navigation {
    /* When on medium sized screens, we center it by evenly distributing empty space around items */
    justify-content: space-around;
  }
}

/* Small screens */
@media all and (max-width: 500px) {
  .navigation {
    /* On small screens, we are no longer using row direction but column */
    flex-direction: column;
  }
}

让我们尝试一些更好玩的,比如Flex项目的灵活性。考虑到“移动先行”,在宽屏时,三列布局,而且页头和页尾都全屏,而在移动端上单列显示,并且不修改DOM结构修改其显示顺序。

.wrapper {
  display: flex;
  flex-flow: row wrap;
}

/* We tell all items to be 100% width */
.header, .main, .nav, .aside, .footer {
  flex: 1 100%;
}

/* We rely on source order for mobile-first approach
 - in this case:
 - 1. header
 - 2. nav
 - 3. main
 - 4. aside
 - 5. footer
 */

/* Medium screens */
@media all and (min-width: 600px) {
  /* We tell both sidebars to share a row */
  .aside { flex: 1 auto; }
}

/* Large screens */
@media all and (min-width: 800px) {
  /* We invert order of first sidebar and main
   - And tell the main element to take twice as much width as the other two sidebars 
   */
  .main { flex: 2 0px; }
  
  .aside-1 { order: 1; }
  .main    { order: 2; }
  .aside-2 { order: 3; }
  .footer  { order: 4; }
}

##浏览器前缀

使用Flexbox,为了支持大多数浏览器可能需要添加前缀。它不仅包括添加浏览器的私有前缀,而且还要使用不同的属性名和属性值。这是因为Flexbox规范随着时间的向前推移有多个版本:最老版本混合版本标准版本

也许通过Autoprefixer语法是最好的方式来处理Flexbox方式和运行你的CSS,这种方式可能是最好的处理方式。

另外,这里有一个Sass写的@mixin帮助你处理一些前缀,你只需要这样做:

@mixin flexbox() {
  display: -webkit-box;
  display: -moz-box;
  display: -ms-flexbox;
  display: -webkit-flex;
  display: flex;
}

@mixin flex($values) {
  -webkit-box-flex: $values;
  -moz-box-flex:  $values;
  -webkit-flex:  $values;
  -ms-flex:  $values;
  flex:  $values;
}

@mixin order($val) {
  -webkit-box-ordinal-group: $val;  
  -moz-box-ordinal-group: $val;     
  -ms-flex-order: $val;     
  -webkit-order: $val;  
  order: $val;
}

.wrapper {
  @include flexbox();
}

.item {
  @include flex(1 200px);
  @include order(2);
}

相关属性

有关于CSS Grid Layout Module相关的中文教程,可以点击这里参阅。

相关资源

推荐阅读的几篇文章:

有关于Flexbox更多的中文教程,可以猛击这里阅读。

Bugs

Flexbox功能虽然强大,但并不是没有问题,至少目前阶段之下,特别是移动端中使用Flexbox还是存在不少问题。幸好Philip Walton和Greg Whitworth收集和整理了有关于Flexbox的Bugs。这是一个开源的项目,可以很好的跟踪有关于Flexbox的问题,所以我认为这是最好的一份资源。

有关于Flexbox的详细讲解可以点击这里进行更多的了解。前端时间在万能的微博知乎也创建相关的链接,希望更多的同学能提供有关于Flexbox在实际项目中使用时踩的坑以及如何填的坑。也更希望大家能在Github中创建和提交Issues

浏览器兼容

前面也简单提到了,Flexbox的语法规范有多个版本,可以拆分成:

  • (最新版本)最近的版本,意味着是最近的一个语法版本(如display:flex;)
  • (混合版本)也是中间版本,2011年提出的一个语法版本,这是一个很奇葩的非官方的语法版本,主要是为IE浏览器提供(如:display:flexbox;
  • (最老版本)2009年的语法版本,也是最老的一个语法版本(如:display:box;

黑莓浏览器10 +支持新语法。

本文是根据CSS-Tricks整理的一篇《A Complete Guide to Flexbox》和W3C中文兴趣小组翻译的Flexbox规范整理。