前言
「看过很多博客文章,对flex布局都没有真正的理解」随着浏览器兼容性的发展,flex布局几乎成为我们日常布局工作的主要工具。搞懂flex布局,以及其每个属性的含义尤为重要,拿我自己举例,看过很多文章博客,也用了很长时间的flex布局,但只是停留在会用那几个属性,不了解其背后的原理,比如flex组合属性中的三个属性,尤其flex-shrink flex-basis 属性,flex布局是否会溢出这些问题,面试的时候真的是一问一个不吱声 😂 ,或者工作中布局稍微变一变就不知该如何设置。
本文仅为个人学习总结内容,有不严谨和待完善的之处。请酌情观看。若有收获,我近期的努力便增加了另外的意义。若有不正确的地方,感谢指出🙏!
前置知识
友情链接,简单了解即可
文档书写模式
developer.mozilla.org/zh-CN/docs/...
float布局
developer.mozilla.org/zh-CN/docs/...
BFC
developer.mozilla.org/zh-CN/docs/...
基本定义
如果是你,你会怎么给他下定义?
说实话我刚开始写的这篇总结的时候时根本不知如何说起,下定义对我来说更难了,这是我搜罗各种资料整理出的来的定义:
Flex布局与流式布局、网格布局等类似,定义了一种新的布局模型,即伸缩布局盒模型,是一种一维的布局模型,能够改变子元素行向或纵向的排列和对齐方式,以及通过对伸缩容器中可用空间的支配,动态调整项目宽、高、顺序等,以最佳的方式填充可用空间,以一种更有效的方式轻松实现多行多列布局。解决了以往使用float和定位的方式实现相同的布局存在的问题。
这是我学习了一阵子自己总结的定义:
Flex布局又称弹性布局模型,Flex容器为其内容创建新的伸缩格式化上下文 (Flex Formatting Context),除了其伸缩特性外,与块级格式化上下文BFC特性相同,都会构建一个独立的布局区域,在这个区域中遵循着Flex布局模型设定的规则,不再遵循标准流的一套新的布局体系,flex布局是一维布局模型,在横向或者纵向上通过动态调整子元素的宽、高(伸缩)以及顺序等,以一种高效便捷的方式通过合理分配父元素的可用空间来调整让子元素分布以及对齐方式,让元素内容得以充分的展现。
现在的我下定义:
Flex布局是子元素划分可用空间的游戏
根据前置知识我们了解到了,设置float属性的元素 相当于创建了一个BFC 对吧 但是设置了flex属性的元素创建了FFC, 所以设置flex属性的元素不再对float属性生效,因为一个元素他不可能同时创建BFC 又创建FFC 你说呢?
基本术语
以下是一些简单却很重要的基本术语,flex布局其他核心概念均基于基本术语描述
图1-1 flex布局术语示意图
主轴[main axis]
主轴是主轴方向的延伸,flex子项在将沿着这条轴线进行布局,根据flex-direction的取值不同,他可能是水平方向也可能是垂直方向。flex布局将从主轴的起始位置开始,朝着终止位置的方向摆放flex子项。
主轴起始位置和终止位置
主轴的起始位置和终止位置与文档书写模式有关,以水平布局为例,如果文档书写模式是从左到右(ltr ),flex布局的起始位置在左,终止位置在右。若文档书写模式是从右向左(rtl),则flex布局的起始位置在右,终止位置在左。developer.mozilla.org/zh-CN/docs/... anyway, 因为文档书写模式可以通过设置来改变,所以这也是我们不用上下左右来描述 flexbox 元素的方向的原因。
主轴长度
主轴长度(main size )取决于flex容器主轴方向,若主轴方向为row,则main size 取值为flex容器元素身上的width/min-width/ max-width属性。若主轴方向为column,则main size 取值为flex容器元素身上的height/min-height/max-height属性。
主轴尺寸/长度 取决于flex容器的这几个属性,也说明长度和宽度是flex容器必不可少的需要设置的属性,若不设置,主轴空间将不存在,更无法实现子项在flex容器中的布局。
交叉轴[cross axis]
垂直于主轴的轴就是交叉轴,也称侧轴(不同资料中叫法略有不同)如图1-1所示,flex子项垂直方向的布局将沿着这条轴线进行。
交叉轴起始位置和终止位置
交叉轴的起始位置在交叉线的起点,如图1-1所示,终止位置在交叉轴方向与容器的交叉点**
**developer.mozilla.org/zh-CN/docs/...
交叉轴长度
交叉轴长度(cross size )取决于flex容器主轴方向,若主轴方向为row,则cross size 取值为flex容器元素身上的height/min-height/max-height属性。若主轴方向为column,则main size 取值为flex容器元素身上的width/min-width/ max-width属性。
以下内容将按照flex布局的属性为脉络逐个详细的剖析每个属性的表现,以及属性间如何相互作用
flexbox属性
dispaly
属性取值:dispaly:flex | inline-flex
基本表现
若一个容器被设置了dispaly:flex | inline-flex 属性,且仅仅被设置了这一属性,其他关于flex布局的属性均未设置时,此时flex容器以及子元素的表现则为flex布局的默认状态或者说初始状态。
在基本术语-主轴空间小节提到,对于flex容器盒子,若不设置宽高,宽可能会继承父元素的宽,或者由内容宽度展现,高则由子元素撑开或者由内容撑开,在这种情况下,无法继续讨论flex布局以及属性发挥的作用,所以接下来我们的讨论均基于flex容器盒子有宽高的情况下。
我们给一个400*100的盒子设置了flex布局的,这个盒子就成为了flex布局的容器盒子,其中的子元素,没有添加任何属性,也没有设置宽高时,表现如下图所示:
- 默认主轴为横轴 flex-direction: row
- 子元素会从主轴的起始线开始水平无间隙的从左到右排列
- 子元素的高度会拉伸至父元素的高度
这是因为align-items的默认值是stretch , 当你仅仅只是希望子元素高度与夫元素相同时,并不需要再给子元素设置高度。若主轴设置为横向,则子元素高度会被自动拉伸至夫盒子的高度,相当于设置了height100%,但若主轴设置为纵向,则元素的宽度被自动拉伸至父亲盒子的高度,相当于设置了width100%,如果子元素本身设置了宽高,则以子元素为准。
- 子元素的宽度为内容的宽度
- 子元素内容较多时,会溢出,而不会换行展示,是因为flex-warp属性默认为:nowarp
display flex 基本表现
Flex溢出情况总结
该小节为对基本表现第5点的扩充,Flex布局设计为弹性布局,直观上或者说概念上,让人感觉不会再有溢出的可能,但情况并非如此,被定义为flex布局的元素仍会溢出。总结为以下3种情况:
- 内容多 且 flex-warp属性为nowarp的时或flex-shrink 属性为0时
- 子元素width空间总和超出父元素时,flex-shrink 属性为0 或者flex-warp: nowarp
- 主轴方向为row,但是元素的高度过高,在交叉轴方向溢出
甚至说在不同情况下 flex-warp: nowarp 和 flex-shrink : 0 效果是一样的。
第一种情况
第二种情况
第三种情况
flex-direction
flex-direction: row | row-reverse | column | column-reverse
flex-direction 属性改变的是主轴方向,在默认从左➡️右的书写模式下:
属性取值为 row | row-reverse 子元素在flex容器中水平方向排列,主轴为水平轴x轴
属性取值为 column | column-reverse 子元素在flex容器中水平方向排列,主轴为垂直方向的y轴
flex-warp
flex-warp : warp | nowarp 默认取值为nowarp,该属性表达的含义是当子元素宽度超出父元素宽度时,是否另起一行展示内容,若设置warp 则折行显示,若nowarp 则溢出。在溢出小节中有提及。
flex-flow
flex-flow 是 flex-direction & flex-warp 的组合属性
justify-content
justify-content: flex-start | flex-end | center | space-between | space-around | stretch
这里重点提下这3个属性:
在每行上均匀分配flex子元素。相邻元素间距离相同。每行第一个元素与行首对齐,每行最后一个元素与行尾对齐。
在每行上均匀分配flex子元素。相邻元素间距离相同。每行第一个元素到行首的距离和每行最后一个元素到行尾的距离将会是相邻元素之间距离的一半。
flex子元素都沿着主轴均匀分布在指定的对齐容器中。相邻flex子元素之间的间距,主轴起始位置到第一个flex子元素的间距,主轴结束位置到最后一个flex子元素的间距,都完全一样。
justify-content 属性设置为不同属性值时的表现如下,以flex-direction:column 为例
align-items
align-items : normal |flex-start | flex-end | center | stretch |baseline
默认值:
align-items 的默认值是stretch, 在flex布局基本表现那一章节就提到过。
align-items 的取值与接下来提到的align-self属性的取值很像。
align-items: 取值为normal时,在正常的flex布局中表现为stretch。
align-item: 取值为basicline时,以文本基线对齐。 这一属性在文本对齐场景下非常有用 比如:
align-item: 取值为stretch时,子元素若设置了固定的高度,stretch属性就不再生效了,可以通过设置了min-width/min-height 实现 stretch ‼️ 该规则也同样适用于flex布局中取值为stretch的其他属性!!
align-content
align-content : flex-start | flex-end | center | space-between | space-around | stretch
align-content 属性仅在发生flex布局设置为warp 且存在换行时生效,其作用时将换行后的每一行看作一个整体,分配交叉轴空间可用空间。通过仔细观察不难发现,align-content属性取值分别为 flex-start | flex-end | center | space-between | space-around | stretch ,和元素设置 flex-direction: column 调整其justfy-content的属性有一曲同工之妙。因为align-content 属性把换行后的每一行都看作一个整体,来分配换行情况下交叉轴的可用空间。
在交叉轴空间中每一行都看成一个整体
就类似于justify-content 属性了 悟了没 再仔细看看官网给的图
drafts.csswg.org/css-flexbox...
flex-items属性
flex
以下讨论均基于flex-direction:row 方向 书写模式为从左到右,该小节我们讨论的flex属性也是一个组合属性,完整的flex属性为:
属性简写的含义:放在最前面 先大致了解下
flex: initial 相当于重置为flex属性的默认值,也就是初始值 0 1 auto。
flex: auto 相当于1 1 auto
flex:none 相当于 0 0 auto 这种情况下 flex-shrink为0 表示溢出时不可伸缩
flex:< positive-number> 我们最常用的就是这种了,例如 flex:1 表示的是 1 1 0, flex:n 表示的是 n 1 0,表示可以伸缩但是flex-basis将不以元素宽度/高度为基准
flex-basis
这里先对flex-basis 进行介绍的意图是引出可用空间的概念,因为flex布局就是一个划分可用空间的游戏,而可用空间又分为正向可用空间和负向可用空间,而剩余的2个属性 flex-grow 和 flex-shrink 则是基于这两种可用空间为基础展开。所以flex-basis 可以说是flex-grow 和 flex-shrink 工作的基准。
可用空间:
先说结论:可用空间完完全全取决于flex-basis属性,flex-basis又是什么,如下表格所示,flex-basis 一共有三种取值,分别为 auto 、0、px 或者说它的取值就是一个像素单位。
可用空间计算公式 = 主轴长度 - 所有flex子元素上的flex-basis属性取值
记住这个表格,接下来计算可用空间即使根据此表‼️
说到底 flex-basis 和width /height 一样 是一个取值为长度单位的属性,而这个值是flex布局【伸】【缩】的基准值。
为了更好的理解flex-basis属性以及由该属性产生的可用空间概念,需要配合flex-grow属性,所以这里先简单介绍下,flex-grow 属性含义为flex子元素的膨胀系数,当你试图设置这个属性的时候,一定是你想让当前子元素比现在的它占用空间更多的时候,通过给flex子元素设置flex-grow属性后,当前子元素将会沿着主轴方向增长,吃掉主轴上更多的(可用空间/剩余空间)
我们使用单一变量原则 举例说明,现将flex属性其他两个属性flex-grow flex-shrink 固定为1,只更改flex-basis,看看我们可以得到什么结论?
(其中flex-shrink在主轴方向溢出的情况下才起作用,这里我们举的例子不出现溢出情况,所以可以暂不考虑flex-shrink属性,可以假装其不存在。)
假设这样一个场景:一个400*400的父元素中有3个flex子元素,3个子元素的宽度分别为50 100 150。
xml
<style>
.flexbox{
box-sizing: border-box;
display: flex;
width: 400px;
height: 400px;
border: 1px solid;
}
.flexbox>div{
border: 1px solid;
/* flex: 1 1 0; */ sutiation one
/* flex: 1 1 auto; */ sutiation two
/* flex: 1 1 100px; */ sutiation tree
}
.flexbox>div:nth-child(1){
width: 50px;
}
.flexbox>div:nth-child(2){
width: 100px;
}
.flexbox>div:nth-child(3){
width: 150px;
}
</style>
<div class="flexbox">
<div>1</div>
<div>2</div>
<div>3</div>
</div>
- flex: 1 1 0
这种情况下 flex 3个子元素的 flex-basis属性均为0,所以所主轴全部长度均为可用空间,在flex-grow相同都为1时,3个元素平分主轴空间得到的三个长度相同的表现,这个时候,flex子元素原本的width是多少已经不再重要‼️, flex-basis为0,(主轴长度-0+0+0)可用空间为全部主轴空间。
以上这种设置等同于上文提到的flex简写的最后一种方式,也就是我们经常见到的flex:< positive number>
这是我们看到主轴空间应如何分配,即使这3个子元素分别设置了不同的flex属性值,比如他们分别是 flex:1 flex:2 flex:3 他们也是会按照比例划分全部的主轴空间去展示。
NOTE: flex-basis为0 主轴为全部可用空间,flex子元素原本的width是多少已经不再重要‼️
- flex: 1 1 auto
flex-basis 取值为auto时,则展现出完全不同的表现,因为这个时候可用空间 = 400-(50+100+150)= 200px 而每个元素的flex-grow为1 则平分这200px的可用空间,所以每个元素在自己元素width的基础上加200/3 得到现在我们看到的表现。
- flex: 1 1 100px
我们来一起看下第三种情况,这种情况表现和第一种情况一样,但其背后的道理却不同,当我们给每个子元素设置了flex-basis属性为一个固定值100px,我们还是看他的可用空间,400-(100+100+100) = 100 主轴将还有100px的可用空间供3个子元素分配,如何分配,看flex-grow,此时flex-grow 属性均为1 则每个元素分33.3px 而此时每个元素的伸缩基准值为100px,每个元素在100px的基准+33.3px,观察阴影部分均为100px,而剪头所指为在此基准上增加的33.3px
在这种情况下,flex子元素原本设置的宽度,也已经不再重要了。只有flex-basis :auto的情况下才根元素本身设置的宽度/高度有关,其余2种情况则无关。
以上3张图 看阴影部分的变化,其实不用借助什么别的工具,浏览器本身就把关键信息标注的很清晰了,只是我平时可能忙于业务开发,都没有发现。
flex-grow
含义为flex子元素的膨胀系数,当你试图设置这个属性的时候,一定是你想让当前子元素比现在的它占用空间更多的时候,通过给flex子元素设置flex-grow属性后,当前子元素将会沿着主轴方向增长,吃掉更多的(可用空间/剩余空间),默认值是0。
理解了flex-basis后,被设置flex-grow属性flex子元素将如何膨胀,就变得很明白,计算出主轴的可用空间后,当前子元素在flex布局中最终的展现 = 自己的flex-basis属性取值 + 主轴可用空间 ✖️
所以当你仅仅是想要每个子元素都按比例划分整个主轴空间时,就尽管设置成flex:< positive number> 的形式,就将因为这种简写形式默认的flex-basis为0,主轴空间全部为你所用,任你分配。若想要所有元素平分,flex属性设置成一样的整数,这个整数就对应flex-grow增长系数,不在乎这个整数是1 还是100,我们这里计算的是比例。
还有一种情况,就是flex容器中有几个子元素,有的设置了flex属性,有的没有设置,没有设置flex属性的子元素的将拥有其默认值,也就是flex:0 1 auto, 注意这里的关键信息 flex-basis取值为auto;
如下图所示,就是经典的搜索框布局,400*100的父盒子,其中第二个蓝色盒子没有设置flex属性,但设置了width:100px 这就告诉我们第二个元素flex-basis为100px,第一个盒子flex:1,之前提到的简写,flex:1 代表 第一个盒子flex-basis :0 ,这种情况下可用空间如何计算,类推,400-100 = 300px,而这300px的可用空间只有第一个盒子设置了flex-grow属性,那么第一个盒子就要吃掉全部的可用空间,在这种情况下,若动态的增加或者减少第二个盒子的宽度,第一个盒子就会出现此消彼长的情况。第一个盒子就是所有可用空间体现。 当前这里还可以扩展form表单的布局,其实道理都是一样的。
flex-shrink
flex-shrink属性则正好与flex-grow相反,flex-shrink属性定义的是收缩比例,只在flex items 设定空间大于父元素的时候,元素发生收缩时才生效,默认值为1,表示溢出时会收缩。 理解了正可用空间,负可用空间也变得极容易理解了,如图所示 宽度为400px的容器盒子,定义每个子元素的宽度为200px,负可用空间为 400-200*3 = -200px,
每个元素收缩的比例相同,每个元素都收缩 200/3 px。如图所示箭头所指部分为收缩部分。
当我依次对以上3个子元素依次设置了 flex-shrink 属性为 1 2 3 ,他们的表现如下
负可用空间 200px 每个元素在其flex-basis基础上进行收缩,
第一个元素承担的收缩空间为 -200*1/6
第二个元素承担的收缩空间为 -200*2/6
第三个元素承担的收缩空间为 -200*3/6
如上图箭头所指的区域即为每个子盒子的收缩区域。
Notice ‼️ 与flex-grow 增长属性不同的是,不论如何flex-shrink都不会将子项的宽/高收缩为0,最大程度收缩到其最小内容宽度/高度。
order
属性 的应用场景
The items are then placed in the visual order according to that integer, lowest values first. If more than one item has the same integer value, then within that group the items are laid out as per source order.
通过设置order属性,可以显式的调整flex子元素的摆放顺序,除了通过设置flex-direction改变
[row row-reverse] 改变顺序的改变元素的顺序外,order属性可以单独的为某个子元素设置其摆放顺序
order属性遵循以下原则:
- 默认值为0
- 取值为整数
- 值越小位置越靠前
- 取值相同按照顺序摆放
举例:
以下5个元素的order 取值为
- Source item 5: order: 1
- Source item 4: order: 1
- Source item 3: order: 2
- Source item 2: order: 3
- Source item 1: order: 3
重要应用‼️:
因为order属性默认取值为0,所以如果你想让某个元素放到子元素最后面时,你就随便给一个正整数,这时,当前设置的元素order取值一定是最大的,会摆放到所有元素的最后面。与之相反的,如果你想要某个元素放到所有子元素最前面时,可以通过设置order:-1 实现。
align-self
align-items 属性是影响flex元素在交叉轴上的布局,而 align-self 属性是单独对某一个元素调整其在交叉轴上的布局,align-items是整体调整,而align-self是单独调整,对比学习的话,会不会清晰很多,这两个属性的共同点都是调整的在交叉轴上的布局。
默认情况如果如果给flex子元素没有设置了align-self 属性,则会继承父元素设置的align-items 属性,如果给flex子元素设置了align-self 属性,则会覆盖align-items属性。
align-self 失效的几种情况:
- 父元素设置了固定定位 - 脱离文档流
- 当前元素交叉轴方向设置了margin:auto
- 父元素交叉轴方向设置了margin:auto - 被覆盖
目前学习中遇到这3种失效的情况
gap属性
我们在使用flex布局时候是不是经常有个困扰,就是说布局时候,我们希望相邻元素之间有个相同的margin,从而隔离开不同的元素,通常情况下我们又不希望这个margin作用在开始元素的👈margin和最后一个元素的最后一个right margin,这样我们还得通过:first- child :last-child 选中第一个和最后一个,将他们的margin值设置为0,
登登登登,现在不必这么麻烦了 一个gap属性统统搞定,若flex布局主轴方向为水平方向,则使用row-gap属性,若flex布局主轴方向为垂直方向,则使用column-gap属性,
若是子元素没有相邻元素,就没有间隔的说法了,gap属性在当前元素的 4个边距生效,就是当前元素的4个方向的(上下左右)有元素相邻才生效,若没有了,就不会生效。
如图所示箭头指向的地方都没有gap
gap 属性在flex布局中的兼容性:
固定定位对flex布局的影响
- flex-container 元素固定定位不影响除 align-self 属性外 其他flex属性发挥作用⚠️
- flex-item元素如果设置成固定定位,则不参与flex布局也不占用主轴空间
总结
所有属性总结:
一些遗留问题:
1.目前项目中还没遇到过关于文档书写模式的问题,我尝试改变[writing-mode]的属性值(developer.mozilla.org/zh-CN/docs/...) ,但是横向布局时,flex-direction: row也就是水平方向的时候,writing-mode只有一个horizontal-tb的属性,设置这个属性默认文档书写模式默认就是从左到右的,也无法变换成从右向左的模式了,但是在垂直布局的时候确实可以通过vertical-rl vertical-lr属性调整文档的书写方向。说实话我真搞不懂mdn这个图是怎么来的。
图1-2 文档书写模式
2. align-self 有几种会失效的情况,为什么?
参考:
drafts.csswg.org/css-flexbox...
developer.mozilla.org/zh-CN/docs/...
《图解css3》
本文就到此结束了,希望大家共同努力,早日拿下 CSS 💪💪。
如果文中有不对的地方,或是大家有不同的见解,欢迎指出 🙏🙏。
如果大家觉得所有收获,欢迎一键三连💕💕。