本篇没有流水账,关于基础知识的学习请移步MdnWebDoc
Flex为什么存在
在flex布局出现之前,布局的实现方式往往通过float浮动和position定位的方式实现,因此出现了许多问题:
- 浮动脱离文档流导致的重叠、清除浮动、高度坍塌问题:简单的布局却需要非常多的浮动清除!还要提防子元素都浮动导致父元素坍塌,这太让人头大了!
- 垂直水平居中,特别是垂直居中,实现起来并不容易!我仅仅是想垂直居中而已!?需要这么复杂?
- 行内样式的垂直边距不可用且高度和宽度不能指定,外边距折叠问题。。。
以上种种导致简单的布局却需要大量的代码实现,而flex布局则是破局者;
值得一提的是:虽然W3C官网对于
CSS Flexible Box Layout Module Level 1的规范始终没有转换为Recommendation正式标准,而是一直处于Candidate Recommendation候选标准,但是Flex已经被各大主流浏览器广泛支持
Flex容器本身还是块级元素吗?
<div style="display: flex"></div>
此处需要注意:flex布局只是影响其内部元素的布局,对于与容器同级的元素,其表现和普通的块级元素没有区别
同时有:
<div style="display: inline-flex"></div>
可以让容器对同级元素遵守行内元素的规则,但是与纯正的inline不同的是,其高度和宽度以及垂直边距都会生效
然而这并不代表直接指定display:flex会让容器抛弃块级元素的特性,例如此时容器默认仍占满父元素,垂直方向仍会发生外边距折叠。
一维布局以及为什么在Flex之外还需要有Grid
flex是一维布局,这代表其有主轴的概念,这也就决定了其在交叉轴上的其他行只能靠主轴上的元素溢出得到,这导致了其在部分场景下的局限性
如:
<div class="div-container">
<div class="div-child">123</div>
<div class="div-child">123</div>
<div class="div-child">123</div>
<div class="div-child">123</div>
<div class="div-child">123</div>
<div class="div-child">123</div>
<div class="div-child">123</div>
</div>
<style>
.div-container {
width: 700px;
height: 300px;
border: 1px solid red;
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
}
.div-child {
box-sizing: border-box;
width: 200px;
height: 100px;
border: 1px solid green;
}
</style>
如果我想让最后一个div不在最前方,而是最后面,只能通过margin-left:auto边距的形式,这还好说,可是如果最后一行有两个元素,我想让他们像space-around一样分布呢?好的办法是将他们分别放在多个div,可是这增加了html结构的复杂性,因此grid布局就有存在的意义。
简单点理解,其实就是
flex没有justify-self,至于为什么没有,请见flex存在网格?
从Grid和Flex通用属性看网格和元素在容器中的分布
一般指以下几个属性
justify-contentjustify-itemsjustify-selfalign-itemsalign-selfalign-content
你也许注意到了:好像
grid和flex有些属性是通用的?
justify-content和justify-items好像很像?为什么需要有两个差不多的属性存在?
对于以上问题请见flex存在网格?
Flex布局中的元素还是普通的块级元素吗?
尺寸
当未指定flex-basis和width属性时,flex中元素默认的宽度是其内容的宽度
当未指定元素height时,align-items:sketch时,且未发生换行,元素高度为容器高度
当未指定元素height时,align-items:sketch时,且发生换行,元素高度为其网格高度
当取其他值时,元素高度为其内容高度
可见尺寸不遵循块级元素撑满父元素的特性
为什么flex布局中会有网格?请见flex存在网格?
像是行内元素?还是块级元素?
flex布局中的元素默认表现得像是不会换行的块级元素,且不会发生外边距折叠现象
因此其既不是行内元素,也不是块级元素,行内元素和块级元素只是常规流布局中的特殊表现形式,并不适用于flex
flex属性
是一个容易引起歧义的属性
为flex-grow,flex-shrink,flex-basis的简写形式
决定了元素的固定尺寸,拓展和缩小规则,以及元素实际占据的空间
flex-basis
本意是指定元素的初始尺寸
有以下特点
- 默认值是
auto,如果指定了width,则使用width,没有指定width则使用内容宽度 - 该值为
content时,会元素内容的宽度,即使已经设置了宽度。 - 指定该值时,其优先级高于
width - 默认值为
auto - 该值并不是决定的元素实际宽度,而是固定宽度,元素的实际宽度应该加上根据
grow,shrink分配的部分
flex-grow
flex-grow接收比例值,这个属性的意义为"当众子元素元素的固定宽度不足以撑满父元素宽度时,按照什么样的比例分配剩余空间",当为0时,不会分配剩余空间
固定宽度指的是
flex-basis的取值
分配算法:
- 确定父容器的主轴空间和子元素的
flex-basis - 计算剩余空间:用父容器的主轴空间减去所有子元素的
flex-basis总和(以及外边距、边框和内边距等所占空间),得到剩余空间 - 计算
flex-grow总和 - 按比例分配剩余空间:对于每个设置了
flex-grow值大于0的子元素,按照其flex-grow值占flex-grow总和的比例来分配剩余空间。计算公式为: 子元素分配到的额外空间 = 剩余空间 × (子元素的flex-grow值 /flex-grow总和)
子元素最终的主轴content大小:子元素的 flex-basis + 子元素分配到的额外空间
子元素最终的主轴占据空间 = 子元素的 flex-basis + 子元素分配到的额外空间+padding+margin+border
需要注意的是,元素的
content指的是元素除了padding,border,margin的实际内容区域;box-sizing决定的是width是否包含border和padding;background-clip决定的是背景区域是否延伸到padding,border还是只在content又或者是只存在于text中。todo:新开一篇说一下这块。
flex-shrink
flex-shrink接受比例值,代表众子元素宽度超出父元素宽度时,如何缩小子元素
有趣的是,
flex-shrink与flex-grow行为并不不相同,flex-shrink表示缩小因子,而并非flex-grow的比例。
计算算法:
- 确定父容器的主轴空间和子元素的
flex-basis - 计算溢出空间:用所有子元素的
flex-basis总和(包含外边距、边框和内边距等所占空间)减去父容器的主轴空间,得到溢出空间。 - 计算加权收缩因子总和:公式为:加权收缩因子 = 子元素的
flex-shrink值 × 子元素的flex-basis(或内容宽度)。然后将所有子元素的加权收缩因子相加,得到加权收缩因子总和。 - 按比例收缩元素:对于每个设置了
flex-shrink值大于0的子元素,按照其加权收缩因子占加权收缩因子总和的比例来收缩空间。计算公式为: 子元素收缩的空间 = 溢出空间 × (子元素的加权收缩因子 / 加权收缩因子总和)
子元素最终的主轴content大小 = 子元素的 flex-basis - 子元素收缩的空间
子元素最终的主轴占据空间 = 子元素的 flex-basis - 子元素收缩的空间+padding+margin+border
我们会发现,不同于
flex-grow相同的值会分配相同的额外空间,flex-shrink更倾向于对于基础宽度大的元素更多的缩小,这其实和我们的直觉的相同的:如果小元素和大元素缩小的空间相同,那么小元素很快就消失不见,而此时大元素才刚刚缩小了一点!
flex-shrink更倾向于给出一个合理且符合直觉的结果!
注意:
border会随着屏幕缩放改变其值!而margin,padding,width则不会,尽管其在屏幕上实际占据的屏幕像素发生了改变,其在devtools中的值也不会发生改变。todo:屏幕的实际像素和devtools中的像素有什么关系?
两者默认值以及其导致的一些奇怪的现象
flex-grow默认值是0,这就代表如果主轴有剩余空间,其不会自动拓展
flex-shrink默认值是1,这代表如果元素溢出,默认情况下会收缩以符合容器宽度
因为flex-shrink的默认值是1,因此需要注意有时候明确需要溢出的情况:
<div class="container">
<div class="child"></div>
<div class="child"></div>
</div>
<style>
.container {
width: 300px;
height: 300px;
border:1px solid red;
display: flex;
flex-direction: column;
overflow: auto
}
.child {
height: 200px;
width: 100px;
border: 1px solid blue;
}
</style>
此处子元素明显高度大于容器,且overflow: auto表示代码意愿是希望溢出的部分通过滚动条调节,实际效果如下:
由于flex-shrink会对溢出的部分进行收缩,因此子元素不会溢出,如需实现效果,需要为每个子元素加上flex-shrink:0;
flex:1
是flex: 1 1 0的缩写,也就是flex-basis:0;flex-grow:1;flex-shrink: 1;,其中有几点需要注意
flex-basis已经被指定,因此width会被忽略flex-shrink不为0,子元素不会溢出容器- 但是如果其内容超过了分配的宽度,则以内容宽度为准,直至内容宽度超出容器宽度
问题:对于多个
flex:1的子元素,其宽度一定相同吗?
答案是:不一定,尽管flex-basis:1会忽略元素的width,以下几种情况都会导致其宽度不同
max-width和min-width的影响下,以该值为准- 存在
padding,margin,border的影响下,flex-grow只是保证了其content相同,但是实际占据的空间不会相同 - 元素的内容宽度大于分配宽度时候,宽度以内容宽度为准,直至达到容器宽度
- 当元素发生换行的时候,新一行的元素会重新计算其宽度
对于以上的第四点:
<div class="container">
<div class="child" ></div>
<div class="child"></div>
<div class="child"></div>
</div>
<style>
.container {
width: 100px;
height: 105px;
border:1px solid red;
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.child {
height: 50px;
width: 100px;
border: 1px solid blue;
flex:1;
min-width: 40px;
}
</style>
换行后的元素会在新一行重新计算其宽度
没办法调节主轴的单个元素的分布吗?
flex布局并没有类似justify-self的属性,因此无法在flex官方属性层级调节
具体原因见flex存在网格?
但是可以通过margin:auto,让外边距自动计算来达到差不多的效果。
如果想要在官方属性层级进行调节,请使用 Grid布局