写给自己看的display: flex布局教程

这篇文章发布于 2018年10月28日,星期日,22:01,归类于 CSS相关。 阅读 143039 次, 今日 2 次 56 条评论

 

//zxx: 本教程所有布局效果(含交互)为实时渲染,若布局异常,可以点击这里访问原文。

一、前言&索引

给div这类块状元素元素设置display:flex或者给span这类内联元素设置display:inline-flex,flex布局即创建!其中,直接设置display:flex或者display:inline-flex的元素称为flex容器,里面的子元素称为flex子项。

//zxx: flex和inline-flex区别在于,inline-flex容器为inline特性,因此可以和图片文字一行显示;flex容器保持块状特性,宽度默认100%,不和内联元素一行显示。

而Flex布局相关属性正好分为两拨,一拨作用在flex容器上,还有一拨作用在flex子项上。具体参见下表,点击可快速索引。

作用在flex容器上 作用在flex子项上

无论作用在flex容器上,还是作用在flex子项,都是控制的flex子项的呈现,只是前者控制的是整体,后者控制的是个体。

其他说明:

  • 本教程所有案例HTML结构为:
    container(flex容器)
      div(flex子项) > img
      div(flex子项) > img
      div(flex子项) > img

    同时,为了便于区分,flex容器区域使用虚框标示,flex子项增加了白蓝径向渐变背景色,图片上显示了原始序号。

  • Flex布局中还有主轴和交叉轴的概念,为避免过多概念干扰,本教程省略相关措辞,而是使用水平方向和垂直方向代替

    //zxx: ① writing-mode属性可以改变文档流方向,此时主轴是垂直方向,但实际开发很少遇到这样场景,因此,初学的时候,直接使用水平方向和垂直方向理解不会有任何问题,反而易于理解。

二、作用在flex容器上的CSS属性

1. flex-direction

flex-direction用来控制子项整体布局方向,是从左往右还是从右往左,是从上往下还是从下往上。和CSS的direction属性相比就是多了个flex

语法如下:

flex-direction: row | row-reverse | column | column-reverse;

其中:

row
默认值,显示为行。方向为当前文档水平流方向,默认情况下是从左往右。如果当前水平文档流方向是rtl(如设置direction:rtl),则从右往左。
row-reverse
显示为行。但方向和row属性值是反的。
column
显示为列。
column-reverse
显示为列。但方向和column属性值是反的。

眼见为实,点击下面对应单选项,可以看到实时的布局效果:

示意图片1
示意图片2
示意图片3

2. flex-wrap

flex-wrap用来控制子项整体单行显示还是换行显示,如果换行,则下面一行是否反方向显示。这个属性比较好记忆,在CSS世界中,只要看到单词wrap一定是与换行显示相关的,word-wrap属性或者white-space:nowrap或者pre-wrap之类。

语法如下:

flex-wrap: nowrap | wrap | wrap-reverse;

其中:

nowrap
默认值,表示单行显示,不换行。于是很容易出现宽度溢出的场景,其渲染表现比较复杂,需要对CSS3宽度有一定了解,可以阅读“理解CSS3 max/min-content及fit-content等width值”这篇文章。具体表现如下(以水平布局举例):

  • flex子项最小内容宽度min-content之和大于flex容器宽度,则内容溢出,表现和white-space:nowrap类似。
  • 如果flex子项最小内容宽度min-content之和小于flex容器宽度,则:
    • flex子项默认的fit-content宽度之和大于flex容器宽度,则flex子项宽度收缩,正好填满flex容器,内容不溢出。
    • flex子项默认的fit-content宽度之和小于flex容器宽度,则flex子项以fit-content宽度正常显示,内容不溢出。

在下面案例中,示意的图片默认有设置max-width:100%,flex子项div没有设置宽度,因此,flex子项最小宽度是无限小,表现为图片宽度收缩显示。如果我们取消max-width:100%样式,则此时flex子项最小宽度就是图片宽度,就可以看到图片溢出到了flex容器之外。

wrap
宽度不足换行显示。
wrap-reverse
宽度不足换行显示,但是是从下往上开始,也就是原本换行在下面的子项现在跑到上面。

眼见为实,点击下面对应单复选框,可以看到实时的布局效果:

示意图片1
示意图片2
示意图片3
示意图片4

3. flex-flow

flex-flow属性是flex-directionflex-wrap的缩写,表示flex布局的flow流动特性,语法如下:

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

当多属性同时使用的时候,使用空格分隔。

举个例子,容器元素如下设置:

.container {
    display: flex;
    flex-flow: row-reverse wrap-reverse;
}

实时效果如下:

示意图片1
示意图片2
示意图片3
示意图片4

可以看到水平排序从右往左(row-reverse属性值的作用),以及换行的那一行在上面(wrap-reverse属性值的作用)。

4. justify-content

justify-content属性决定了水平方向子项的对齐和分布方式。CSS text-align有个属性值为justify,可实现两端对齐,所以,当我们想要控制flex元素的水平对齐方式的时候,记住justify这个单词,justify-content属性也就记住了。

justify-content可以看成是text-align的远房亲戚,不过前者控制flex元素的水平对齐外加分布,后者控制内联元素的水平对齐。

语法如下:

justify-content: flex-start | flex-end | center | space-between | space-around | space-evenly;

其中:

flex-start
默认值。逻辑CSS属性值,与文档流方向相关。默认表现为左对齐。
flex-end
逻辑CSS属性值,与文档流方向相关。默认表现为右对齐。
center
表现为居中对齐。
space-between
表现为两端对齐。between是中间的意思,意思是多余的空白间距只在元素中间区域分配。使用抽象图形示意如下:

space-between分布效果示意

space-around
around是环绕的意思,意思是每个flex子项两侧都环绕互不干扰的等宽的空白间距,最终视觉上边缘两侧的空白只有中间空白宽度一半。使用抽象图形示意如下:

space-around分布效果示意

space-evenly
evenly是匀称、平等的意思。也就是视觉上,每个flex子项两侧空白间距完全相等。使用抽象图形示意如下:

space-evenly分布效果示意

眼见为实,点击下面对应单复选框,可以看到实时的布局效果:

示意图片1
示意图片2
示意图片3

5. align-items

align-items中的items指的就是flex子项们,因此align-items指的就是flex子项们相对于flex容器在垂直方向上的对齐方式,大家是一起顶部对齐呢,底部对齐呢,还是拉伸对齐呢,类似这样。

语法如下:

align-items: stretch | flex-start | flex-end | center | baseline;

其中:

stretch
默认值。flex子项拉伸。在演示中我们可以看到白蓝径向渐变背景区域是上下贯穿flex容器的,就是因为flex子项的高度拉伸到容器高度导致。如果flex子项设置了高度,则按照设置的高度值渲染,而非拉伸。
flex-start
逻辑CSS属性值,与文档流方向相关。默认表现为容器顶部对齐。
flex-end
逻辑CSS属性值,与文档流方向相关。默认表现为容器底部对齐。
center
表现为垂直居中对齐。
baseline
表现为所有flex子项都相对于flex容器的基线(字母x的下边缘)对齐。

眼见为实,点击下面对应单选框,可以看到实时的布局效果:

x

示意图片1
示意图片2
示意图片3

6. align-content

align-content可以看成和justify-content是相似且对立的属性,justify-content指明水平方向flex子项的对齐和分布方式,而align-content则是指明垂直方向每一行flex元素的对齐和分布方式。如果所有flex子项只有一行,则align-content属性是没有任何效果的。

语法如下:

align-content: stretch | flex-start | flex-end | center | space-between | space-around | space-evenly;

其中:

stretch
默认值。每一行flex子元素都等比例拉伸。例如,如果共两行flex子元素,则每一行拉伸高度是50%。
flex-start
逻辑CSS属性值,与文档流方向相关。默认表现为顶部堆砌。
flex-end
逻辑CSS属性值,与文档流方向相关。默认表现为底部堆放。
center
表现为整体垂直居中对齐。
space-between
表现为上下两行两端对齐。剩下每一行元素等分剩余空间。
space-around
每一行元素上下都享有独立不重叠的空白空间。
space-evenly
每一行元素都完全上下等分。

眼见为实,我们给flex容器设置高度500像素,然后点击下面对应单选框,可以看到实时的布局效果:

示意图片1
示意图片2
示意图片3
示意图片4
示意图片5
示意图片6
示意图片7

三、作用在flex子项上的CSS属性

1. order

我们可以通过设置order改变某一个flex子项的排序位置。

语法:

order: <integer>; /* 整数值,默认值是 0 */

所有flex子项的默认order属性值是0,因此,如果我们想要某一个flex子项在最前面显示,可以设置比0小的整数,如-1就可以了。

眼见为实,下面flex容器有3个子元素,现在,我们给第2个子元素设置order属性值,看看其排列位置有何变化。点击下面的单选框,可以看到实时的交互效果:

示意图片1
示意图片2
示意图片3

2. flex-grow

flex-grow属性中的grow是扩展的意思,扩展的就是flex子项所占据的宽度,扩展所侵占的空间就是除去元素外的剩余的空白间隙。

具体的扩展比较复杂。在展开之前,我们先看下语法。

语法:

flex-grow: <number>; /* 数值,可以是小数,默认值是 0 */

flex-grow不支持负值,默认值是0,表示不占用剩余的空白间隙扩展自己的宽度。如果flex-grow大于0,则flex容器剩余空间的分配就会发生,具体规则如下:

  • 所有剩余空间总量是1。
  • 如果只有一个flex子项设置了flex-grow属性值:
    • 如果flex-grow值小于1,则扩展的空间就总剩余空间和这个比例的计算值。
    • 如果flex-grow值大于1,则独享所有剩余空间。

    具体可参见下面“grow案例1”。

  • 如果有多个flex设置了flex-grow属性值:

    • 如果flex-grow值总和小于1,则每个子项扩展的空间就总剩余空间和当前元素设置的flex-grow比例的计算值。
    • 如果flex-grow值总和大于1,则所有剩余空间被利用,分配比例就是flex-grow属性值的比例。例如所有的flex子项都设置flex-grow:1,则表示剩余空白间隙大家等分,如果设置的flex-grow比例是1:2:1,则中间的flex子项占据一半的空白间隙,剩下的前后两个元素等分。

    具体可参见下面“grow案例2”。

grow案例1:
flex容器有3个子元素,现在,我们仅给第2个子元素设置flex-grow属性值,看看其占据尺寸有何变化。点击下面的单选框,可以看到实时的交互效果:

示意图片1
示意图片2
示意图片3

此实例演示中,仅一个flex子项设置了flex-grow属性值,当我们选择0.5的时候,值小于1,剩余空间用不完,因此,扩展的宽度是总剩余宽度是0.5,也就是一半;当我们选择1的时候,正好所有空间都使用;当我们选择2的时候,效果一样,因为没有其他参与分配的子项,因此渲染表现和1一样。

grow案例2:
flex容器有3个子元素,默认所有子项都设置了flex-grow:0.25,现在我们点击下面的单选框,改变第2个子元素的flex-grow属性值,看看其占据尺寸有何变化:

示意图片1
示意图片2
示意图片3

此实例演示中,因为3个子项都是0.25,因此默认还剩余25%的剩余空间;如果我们选择flex-grow:0,则加起来的flex-grow0.5,因此剩余50%空间;如果我们选择flex-grow:0.5,则加起来的flex-grow1,因此没有剩余空间,同时空间占用比例为1:2:1,最终效果符合此预期;如果我们选择flex-grow:1,则加起来的flex-grow大于1,剩余空间按比例分配,为1:4:1,最终效果也确实如此。

以上就是flex-grow属性的作用规则。

3. flex-shrink

shrink是“收缩”的意思,flex-shrink主要处理当flex容器空间不足时候,单个元素的收缩比例。

语法如下:

flex-shrink: <number>; /* 数值,默认值是 1 */

flex-shrink不支持负值,默认值是1,也就是默认所有的flex子项都会收缩。如果设置为0,则表示不收缩,保持原始的fit-content宽度。

flex-shrink的内核跟flex-grow很神似,flex-grow是空间足够时候如何利用空间,flex-shrink则是空间不足时候如何收缩腾出空间。总有点CP的味道。

两者的规则也是类似。已知flex子项不换行,且容器空间不足,不足的空间就是“完全收缩的尺寸”:

  • 如果只有一个flex子项设置了flex-shrink
    • flex-shrink值小于1,则收缩的尺寸不完全,会有一部分内容溢出flex容器。
    • flex-shrink值大于等于1,则收缩完全,正好填满flex容器。
  • 如果多个flex子项设置了flex-shrink
    • flex-shrink值的总和小于1,则收缩的尺寸不完全,每个元素收缩尺寸占“完全收缩的尺寸”的比例就是设置的flex-shrink的值。
    • flex-shrink值的总和大于1,则收缩完全,每个元素收缩尺寸的比例和flex-shrink值的比例一样。下面案例演示的就是此场景。

眼见为实,flex容器有4个子元素,现在,我们给第2个子元素设置不同的flex-shrink属性值,看看其占据尺寸有何变化。点击下面的单选框,可以看到实时的交互效果:

示意图片1
示意图片2
示意图片3
示意图片4

此实例演示中,因为4个子项都是1,和远大于1,因此,完全收缩,不会有内容溢出。如果我们选择flex-shrink:0,则第2个flex子项不收缩,剩下3个flex子项等比例收缩;如果我们选择flex-shrink:1,则4个子项1:1:1:1收缩;如果我们选择flex-shrink:2,则完全收缩尺寸比例分配为1:2:1:1,第2个flex子项收缩的宽度最大,是其他元素的2倍。

以上就是flex-shrink属性的作用规则。

4. flex-basis

flex-basis定义了在分配剩余空间之前元素的默认大小。相当于对浏览器提前告知:浏览器兄,我要占据这么大的空间,提前帮我预留好。

语法如下:

flex-basis: <length> | auto; /* 默认值是 auto */

默认值是auto,就是自动。有设置width则占据空间就是width,没有设置就按内容宽度来。

如果同时设置widthflex-basis,就渲染表现来看,会忽略width。flex顾名思义就是弹性的意思,因此,实际上不建议对flex子项使用width属性,因为不够弹性。

当剩余空间不足的时候,flex子项的实际宽度并通常不是设置的flex-basis尺寸,因为flex布局剩余空间不足的时候默认会收缩。

实例一则:
flex容器有3个子元素,现在,我们给第2个子元素设置不同的flex-basis属性值,看看其占据尺寸有何变化。点击下面的单选框,可以看到实时的交互效果:

示意图片1
示意图片2
示意图片3

选择最后一个flex-basis:256px会发现flex子项的宽度并不是256px,这是因为此时剩余空间不足,3个子项1:1:1收缩的缘故。

5. flex

flex属性是flex-growflex-shrinkflex-basis的缩写。

语法:

flex: none | auto | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]

其中第2和第3个参数(flex-shrinkflex-basis)是可选的。默认值为0 1 auto

案例演示:

第2个flex子项设置flex:none或者flex:auto,我们看看实时布局效果会有怎样的变化:

示意图片1
示意图片2
示意图片3
示意图片4

此时第2个flex子项的flex-growflex-shrinkflex-basis属性值分别是01,和auto

经过上面一番测试,我们可以得到如下结论:

  • flex默认值等同于flex:0 1 auto
  • flex:none等同于flex:0 0 auto
  • flex:auto等同于flex:1 1 auto

6. align-self

align-self指控制单独某一个flex子项的垂直对齐方式,写在flex容器上的这个align-items属性,后面是items,有个s,表示子项们,是全体;这里是self,单独一个个体。其他区别不大,语法几乎一样:

align-self: auto | flex-start | flex-end | center | baseline | stretch;

唯一区别就是align-self多了个auto(默认值),表示继承自flex容器的align-items属性值。其他属性值含义一模一样,如下案例示意:

首先我们设置flex容器baseline对齐,然后点击下面的单选框,给第2个flex子项设置不同align-self属性值,观察其表现:

.container {
    display: flex;
    align-items: baseline;
    height: 240px;
}

x

示意图片1
示意图片2
示意图片3

四、其他Flex知识点

  • 在Flex布局中,flex子元素的设置floatclear以及vertical-align属性都是没有用的。
  • Flexbox布局最适合应用程序的组件和小规模布局(一维布局),而Grid布局则适用于更大规模的布局(二维布局),关Grid布局请参见“写给自己看的display: grid布局教程”一文
  • 已经8102年了,Flex老语法不用在管了,舒爽弃之,然后私有前缀也不用再加了,看到就烦。

本教程优点在于交互效果可以实时体验,更直观。如果是转载文章,必定没有效果,访问原文即可。

说实话,自己之前Flex布局用得很少,本文内容自己也是边学边写,文中若有表述不准确的地方欢迎指正。

感谢阅读!

参考文章:A Complete Guide to Flexbox

(本篇完)

分享到:


发表评论(目前56 条评论)

  1. CSS小玩家说道:

    大神,请教justify-content:flex-start,start,left有啥区别?

    • 张 鑫旭说道:

      目前正常情况下没什么区别(之前有支持和不支持的区别),在使用direction属性改变水平流向的时候会出现差异。

  2. yang说道:

    flex布局下其子元素默认就没有流体特性了

  3. 小毅说道:

    《如果所有flex子项只有一行,则align-content属性是没有任何效果的》,大佬,这里有点小问题,只要容器的宽高是大于item的,都是有效果的。交叉轴方向没有效果是因为很多时候,容器高度和子项高度一样

    • zhenzhenChange说道:

      align-content 是针对多行弹性元素行的,只有配合换行(flex-wrap: wrap)的时候才有作用。

  4. flex-direction: column 高度问题说道:

    父级设置flex-direction: column; 且高度固定,超出高度内容会向右排列,但是超出高度的部分 不会被计算高度 求大神帮解答一下

  5. wwx说道:

    写的很好,点赞!

  6. robert说道:

    请问一下,flex-direction这个里的示例,图片最底部和div为啥会有一小段距离?F12找了一下,没看明白是哪个CSS在起作用,希望能解答一下,谢谢

    • deepsea说道:

      img有个vertical-align 设置为 “bottom” 就可以了

    • renmu说道:

      看这篇文章:https://www.zhangxinxu.com/wordpress/2015/08/css-deep-understand-vertical-align-and-line-height/

      所有的行盒内部都有一个0宽的strut节点。

  7. leges说道:

    非常好用,感谢大佬啊,就是 5. align-items 的演示里,混进了一个奇怪的字母 x

  8. 萌萌萌说道:

    flex-shrink中的示例,选择flex-shrink: 2,为什么第二张图片会位置下移

  9. 在远方说道:

    讲的真棒,尤其是加上示例,真的通俗易懂

  10. 犬来八荒说道:

    讲的太好了吧,这么通俗易懂,记住`justify`这个单词,`justify-content`属性也就记住了。真的是从源头教大家记啊

  11. cecil说道:

    请问flex-wrap 在 iphone6 上不兼容吗?

  12. meteor说道:

    『/zxx: ① writing-mode属性可以改变文档流方向,此时主轴是垂直方向,但实际开发很少遇到这样场景,因此,初学的时候,直接使用水平方向和垂直方向理解不会有任何问题,反而易于理解。』

    我咋感觉使用主轴和交叉轴的描述其实更容易理解呢。。。

  13. 小喇叭说道:

    假如是这种情况:

    测试 23232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323

    2

    css:
    .sh1{
    width:400px;
    background-color: lightyellow;
    }
    .sh1 span{
    display: inline-block;
    width: 1500px;
    }
    .sh2{
    width:50px;
    background-color: lightpink;
    }
    sh1溢出了应该怎么处理,一开始的时候空间就是不够的。

  14. JessieXie说道:

    justify-content严格说感觉是不是不该是水平方向,是按主轴方向来说(如不恰当,请大神忽略哦) 🙂

  15. 菲菲说道:

    一忘记,就赶快来复习一下,哈哈哈!真的太方便了!感谢感谢

  16. O(∩_∩)O嗯!说道:

    一看你文章就明白了,示例很棒,之前看了很多文章没搞明白

  17. 传奇10号说道:

    我又 过来了。 重新复习了一下

  18. allen说道:

    打扰了,麻烦问下:
    这个问题有好的解决方式吗? 望回复,谢谢

    https://stackoverflow.com/questions/33454533/cant-scroll-to-top-of-flex-item-that-is-overflowing-container

  19. Dshoushou说道:

    “align-content stretch 默认值。每一行flex子元素都等比例拉伸。例如,如果共两行flex子元素,则每一行拉伸高度是50%。”

    这里【则每一行拉伸高度是50%】是除了内容高度之外的空白区域高度一样吧。我测试的时候两行内容高度不一样,拉伸之后他们的高度就不一样,但是空白区域高度是一样的。

  20. Dshoushou说道:

    align-content

  21. 单桔说道:

    受教了

  22. 无下限的王五说道:

    对3. flex-shrink的规则有几点不同的看法
    1.不可能只给一个flex子项设置flex-shrink,其他flex子项会有flex-shrink的默认值,为1;可以把其他flex子项的flex-shrink设为0,再设置另一个flex子项的flex-shrink值。
    2.当其他flex子项flex-shrink: 0;另一个flex子项的flex-shrink值大于等于1,不一定收缩完全,要考虑flex子项的宽度(尺寸),当溢出容器的空间大于flex子项的宽度时,flex子项会完全收缩,仍然会有空间溢出。
    3.如果多个flex子项设置了flex-shrink,且flex-shrink值的总和大于1,也不一定能完全收缩,当其中一个flex子项收缩完全时,有可能仍然会有空间溢出
    4.每个元素收缩尺寸占“完全收缩的尺寸”的比例不是flex-shrink值,比例与flex子项自身的宽度(尺寸)有关,可以参考谢然大大的文章

  23. zhyt说道:

    学习了,非常感谢!

  24. hhhh说道:

    很好,学习了

  25. 很好说道:

    很好

  26. ranjing说道:

    建议作者使用不常用属性的时候,备注一下兼容性问题,毕竟很多人可能看到了就拿去开发使用,结果发生手机上不支持,或者某些版本的谷歌都不支持。比如这里的justify-content : space-evenly , 在除了Firefox Mobile (Gecko) 52.0 (52.0) 版本以上支持,其余浏览器,手机浏览器 都是No support

  27. 夜晚硬邦邦说道:

    发现了个Flex布局的在线游戏https://flexboxfroggy.com/

  28. 外评网说道:

    每当遇到pc项目,还是得老老实实用float、 position

  29. 阿桂说道:

    完全看不懂

  30. ub说道:

    `justify-content属性决定了水平方向子项的对齐和分布方式。` 应该是主轴方向,flex-direction 属性决定主轴方向(默认row: 水平方向,column:垂直方向)。
    示例:https://codepen.io/ubbcou/full/yRmMPN/

  31. 许海琼说道:

    一直看你的文章,受教很多,开启我的自学之路啊,

  32. zx说道:

    前段在线资源里的jquery怎么没有了 我经常用的 鑫哥放出来吧

  33. zc说道:

    display:flex和display:inline-flex有什么区别吗?我平时用都是统一设置display:flex,很少用到display:inline-flex。

  34. Misaya_Q说道:

    期待大佬写一篇关于Gride布局的文章

  35. DeathGhost说道:

    ????????????????哈哈,写的比我详细多了,我前几天也写了scroll-behavior与flex????????????

  36. icenzhao说道:

    最近刚好在团队内部分享flex入门,看看大佬的文章,再看看自己准备的ppt,那个汗啊 哈哈 看完文章感觉有学习了一遍

  37. Beme说道:

    现在一直用 flex 布局,居然会被人鄙视说不会 margin + float 布局。

    我能怎么办?

  38. nicholasurey说道:

    真棒,以前一直对flex敬而远之

  39. meepo说道:

    现在的项目需要兼容IE9 ,用不了flex。

    唉。

    • 幼儿园老大说道:

      最近用flex写的页面,后面说是要兼容IE9以上,真是欲哭无泪,都不知道这么改了

  40. fe_bean说道:

    头排沙发

  41. 网站建设说道:

    很齐全

  42. 紫气东来的紫东说道:

    受教了,有些点平常都没注意到 哈哈