17

疑问

最近遇到个问题:flex:1;flex:1 1 0;是否等价。按照我之前对W3C属性值语法的理解,flex:1;应该是flex:1 1 auto;的缺省写法,剩下的两个flex-shrinkflex-basis的坑应该分别对应1auto。嗯,如果我没记错的话,简写属性(shorthand)有个特性:对于缺省值会重置为对应的单个属性的初始值(详情请参看《CSS权威指南第三版》P130)。这里的flex-shrinkflex-basis就是缺省的单个属性。可是常规测试(弹性子项未填充内容)都不能让人明显感知到flex:1;flex:1 1 0;的区别。那么问题来了:How Can We Feel The Difference?

Try It

首先,确定我们要做什么?通过实验了解flex的缺省auto跟0有何区别。

然后,给出代码(可以去我的Github上找到名为shorthand_flex_test的两个文件查看源代码):

.contain {
    background: #aec;
    height: 100px;
    display: flex;
    justify-content: space-around;/*设置主轴方向均分对齐*/
    flex-flow: row wrap;/*设置正常水平排列*/
    align-items: stretch;/*设置所有弹性子项拉伸到弹性容器高度*/
}
.item {
    border: 3px solid rgba(0,0,0,.2);
    padding: 10px;
    background: #eac;
    color: #fff;
    font-size: 2em;
    font-weight: bold;
    text-align: center;
}

效果图:

clipboard.png

接下来,看实验。

第一步:flex:1;flex:1 1 auto; 根据官方定义,缺省值就是初始值,两个实现出来的效果就应该相同。

给.item设置flex:1;,效果图如下:

clipboard.png

给.item设置flex:1 1 auto;,效果图:

clipboard.png

WTF?不对!

先别急,F12看下设置flex:1;时devtool窗口里的计算值

clipboard.png

再看看设置flex:1 1 auto;时的计算值(这句是废话,请自动省略)

clipboard.png

小结:原来缺省的时候不是取的单项的初始值啊(flex-shrink:1flex-basis:auto)。那么这个0%是不是就等价于0呢?

第二步,这次改为设置flex:1 1 0;

clipboard.png

图还是这样的,可是计算值不一样啊,如下

clipboard.png

小结:其实百分比的计算值是以父类容器的宽度为基数计算的,而长度值0直接取值不用再计算,但是0%0的最终计算值都是0px

解答结果

所以说开始的疑问得到解答了:flex:1;flex:1 1 0;的视觉效果和最终计算值是一样的,只不过是计算过程不同。

扩展实验

Part 1. 既然都做实验了,那么可以“顺便”研究一下,flex的几个常用值的几种写法,这里是drafts.csswg.org的链接。

第一种:flex:initial;等价于flex:0 1 auto;,也等价于flex:0 auto;

给.item设置flex:initial;

clipboard.png

计算值

clipboard.png

小结:这里是auto自适应得到的10%。这个属性声明得到的效果就是使得弹性子项在有多余空间的时候不拉伸,在空间不足时收缩到最小的宽度/高度(由主轴方向决定具体的计算基数)。这里的宽度/高度可能涉及到弹性子项内部的文本内容,内容的有无使得auto跟另一个flex-basis的属性content有差异,后面讲。另外,当设置为这个属性值的时候,弹性容器上设置的主轴方向对齐效果和margin:auto;居中效果才能生效。

第二种:flex:auto;等价于flex:1 1 auto;

给.item设置flex:auto;

clipboard.png

计算值

clipboard.png

小结:这使得弹性子项在有多余空间时拉伸,在空间不足时收缩。这时候弹性子项才有完整的弹性效果,而当多个弹性子项设置不同的flex属性声明时,任何多余空间都将被设置了auto属性值的弹性子项“吸收”掉。

第三种,flex:none等价于flex:0 0 auto;

给.item设置flex:none

clipboard.png

计算值

clipboard.png

小结:这使得弹性子项完全失去弹性效果。效果跟设置auto属性值差不多,但是一旦内容溢出弹性容器,这里的弹性子项是不会收缩的。

Part 2. 继续,看看之前提到的content是怎么回事。

第一步,我们来跟initial的等价值做个对照。initial的就不贴了,Part1的第一种里有。直接看flex:0 1 content;

clipboard.png

计算值

clipboard.png

分析:什么情况?!flex的计算值呢?前面的none都会有计算值的,这里竟然不见了!稍等,可不可以这样理解,在设置content的时候,前面两个值可以忽略?

第二步,设置flex:content;

clipboard.png

计算值

clipboard.png

分析:果然是一毛一样的。BUT,WHY?content到底是干啥子的?

标准里说这个属性值设置会使得弹性子项的宽度/高度直接由其中的内容决定。哦~,难怪对照效果中明显是上面那组的弹性子项“们”是被内容撑开了的。

でも、这岂不是跟不设置flex属性没两样了!书读少勿欺我!保险起见,我只能开“大招”了:

    -webkit-flex: content;
    -moz-flex: content;
    -ms-flex: content;
    -o-flex: content;
    flex: content;

clipboard.png

计算值里还是找不到flex的影子

clipboard.png

第三步,再在第二步的“大招”基础上加个width:100px;

clipboard.png

计算值里还只没影儿

clipboard.png

分析:这里给每个弹性子项设置了固定宽度(因为这水平轴就是主轴),因为没有flex属性的伸展因子flex-grow和收缩因子flex-shrink的影响,每个弹性子项都“老老实实的”按照justify-content: space-around的指令水平对齐了(嘴上说着不要“身体”还是挺老实的嘛,啧啧)。

好吧,只能承认,content是个无效的属性值。对!它并不是属性值。好大一个玩笑(轻点,别打脸)。它只是表示弹性子项的宽度/高度由内容决定,即被内容撑开。而这个撑开的宽度/高度则作为伸展因子和收缩因子的基数进行相应弹性变化的计算。

Part 3. 凭什么说被内容撑开的宽度/高度就是连体因子(舌头打结了)的计算基数?不服来辩!

第一步,先看不设置flex属性时上面对照实验中上面一组的数据(记得“关了”width),从左至右:

clipboard.png

clipboard.png

clipboard.png

可以看到第一个no1和第三个no3的宽度是相同的84.297,第二个是290.875。这里要注意因为box-sizing初始值是content-box,所以内容区宽就是width的取值,边距边框都不用管。

第二步,设置一个伸展因子flex:1 1 auto;这里必须设置flex-basis:auto;因为缺省后就是flex-basis:0%;了,相当于以撑开的宽度/高度作为的基数置零了。效果图就不上了,对应前面的flex:auto;,下面是计算值

clipboard.png

clipboard.png

clipboard.png

可以看到这次的第一个no1和第三个no3的宽度是126.813,第二个是333.391。然后让我们搬出伸展因子的计算公式:

flex-basis + flow-grow / sum( flow-grow ) * remain

这里的flow-grow指的就是flex-flow方向上的伸展因子的数值,而由于设置了flex-basis:auto;主轴的基数flex-basis的计算值应该就是第一步的数据84.297、290.875和84.297;remain则是容器总宽度减去第一步中的所有宽度总和的结果:

665 - ( 84.2969 + 290.875 + 84.2969) - (10 + 3) * 2 * 3 = 127.5312

这里因为是算的弹性容器内的区域所以要把内边距和边框都加上,另外之前在盒模型里显示的84.297不是精确值,最下面的计算值清单里的精确值是84.2969,以免引起后面结果错误。

clipboard.png

最后套入公式:

第二部中第一个和第三个弹性子项的宽:

84.2969 + ( 1 / 3 ) * 127.5312 = 126.8073
(这里跟126.813很接近,但是我找不出那里算漏了的,有知道的朋友麻烦告知,谢谢)

第二个弹性子项的宽:

290.875 + (1 / 3 ) * 127.5312 = 333.3854(待查错)

这一部分先到这里吧。花了我好久时间,好晚了。

总结

flex的缺省值并非是单一属性的初始值,并且还有常用的简写属性值initialautonone;当弹性子项没有设置固定宽度(对于水平的情况,也就是宽度本身是auto的)时,flex-basis如果也是auto,那么flex-basis的使用值就是弹性子项的内容本身撑起来的宽度(对于水平的情况)

工具与环境

  • 操作系统:window 7 ultimate 64bit

  • chrome内核版本:45.0.2454.101

  • 编辑器:Sublime Text 3

参考资料

唠叨

在查找资料过程中,发现一些不错的东西,分享加收藏一下。

先这样了,为了发完这文章熬夜了。熊猫君思密达。
对了代码可以去我的Github上找,说完了。

再补一点信息,一定不要忘了标准的文档开篇说的:

Publication as a Candidate Recommendation does not imply endorsement by the W3C Membership. This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.1


uolcano
158 声望37 粉丝