漫谈标准中CSS浮动令人困惑的部分

时间追溯到4年前,那时刚出道,写了一篇CSS定位机制之一:普通流,转眼4年酱油人生,说好的浮动和绝对定位的篇幅也一拖4年。多少是因为对于熟悉的东西很难提起兴致,但更多还是因为懒惰。

这些年一过,浏览器环境的变化令人欣喜。当年甚少人讨论的BFC等概念,如今也已经说烂了。虽尚未满三十却深感锐气不比当年——说好的第二第三篇浮动和绝对定位应该不会有了,所以就随便聊聊浮动和绝对定位的一些麻烦之处——一些很多人可能不知道的,或者故意略过的,或者困惑的地方。

既然是浮动,那么首先第一个问题,什么是浮动

如果是4年前的我,一定会摆出一堆定义,然后对着各种可能是人尽皆知的特性码很多字。如今,要是在让我解释什么是浮动,我只会说:“浮动===靠边”,并且我觉得我已经找不到比“靠边”更合适的词了。

假如我站在一个方阵队伍的中间,浮动的意思,无非就是靠边站,而且,应该不会有人在别人叫你靠边站的时候特意跑到另外一行队伍里去,这就是其中隐含的一个限制——“浮动发生在当前行”。

大部分时候我们不会思考浮动在RTL情况下会是怎样的,所以不失时机地想象一下RTL的浮动,可以锻炼大脑的抽搐功能,加深印象,效果大抵与错用左手类似。

带过了浮动是什么,下面就是正题了。

浮动外缘高度为零

相当一部分人并不知道,外缘高度为零的浮动不会缩短行框,因为这只是css2.1标准里的一段备注。但如果读过标准的内容,相信对这段话多少会催生出一些疑问:

A line box is next to a float when there exists a vertical position that satisfies all of these four conditions: (a) at or below the top of the line box, (b) at or above the bottom of the line box, (c) below the top margin edge of the float, and (d) above the bottom margin edge of the float.

Note: this means that floats with zero outer height or negative outer height do not shorten line boxes.

定义说,紧挨浮动的行框会缩短以提供浮动元素足够的空间。但所谓的“紧挨浮动的行框”需要同时满足垂直位置的4个条件。当浮动元素的外缘高度为零甚至为负时,行框无法满足上述定义的条件,也就不再受浮动的影响。所以标准这两段是因果关系,“this means”只是额外表明上述定义造成的一种特殊的结果。

当然,个人觉得,“当浮动元素没有外缘高度,不占据空间,也就不需要行框退让”这样的解释也是说的通的。而行框不缩短,在很大程度上使得浮动和绝对定位表现的很相似——内容溢出容器外会直接形成层叠。

同时浮动和绝对定位

这应该是一个愚蠢的问题:如果对一个元素同时使用浮动和绝对定位的话,会怎么样?

<div style="float:left;position:absolute;">葵中剑</div>

结果应该是显而易见的,元素绝对定位。但不能因为这个问题而产生困惑,因为两者可以共存。只是当元素绝对定位之后,浮动的值被计算为none

这个问题可以被更复杂化,从而使得相当一部分人产生困惑:

<div style="display:none;float:left;position:absolute;">葵中剑</div>

也就是display:none;的情况下同时浮动和绝对定位,元素的各个值会如何?

我们知道,当元素display:none;之后,元素的positionfloat都不会被应用(具体可参见标准9.7节)。所以,这里需要分清的是计算值和是否被应用。这个例子里,positionfloat的计算值都不会受到影响(分别是absoluteleft),单单只是没有被应用而已。

现在去掉display:none;positionfloat就被应用在元素上,此时,float的值又将被计算为none

这个问题,问的是标准9.7节,隐含的则是计算值以及值是否被应用之间区别的理解。

CSS浮动最让人困惑的语句

标准里关于浮动部分,最让人困惑的语句莫过于9.5.1里的这段:

But in CSS 2.1, if, within the block formatting context, there is an in-flow negative vertical margin such that the float's position is above the position it would be at were all such negative margins set to zero, the position of the float is undefined.

如果你读过标准想必会对这句话有印象,这段话的上文是浮动行为的精确定义。我在第一次阅读的时候,这段话给我带来的困惑是最大的:“擦,啥玩意啊,undefined?”

造成困扰的原因有二:其一,作为非native speaker,即便自认为英语还不错,对于这种文法错乱加上混合了专有名词的句子,也有点力不从心。其二,这里的undefined并不是指值,而是指标准本身。

实际上,我之后看过很多人对这段内容的翻译,但实际上绝大部分都是硬着头皮来的,翻译出来的内容可能作者自己都未理解到位,词与句都甚为牵强。这个比较无奈,于是我只好把这段话发给一个懂css的老外,让他帮我断句,他是这样回复我的:

haha, terribly written sentence
quite confusing
So basically if you set the margins
the vertical margin
to something negative
-1 -2 etc
If the float position is above where it would be if the margins were at 0
then the position is considered "undefined"

这样就清楚多了,至少我一下子就明白了,这个该死的写出这段话的人究竟想说什么,而我再将其大概概括到最短:“垂直负边距的浮动位置在标准里没定义”。

没错,undefined指的是标准里没有相关的定义。为什么要加这么一段蛋疼的描述呢?9.5.1节那9条精确定义了浮动行为,其中,第5条第6条:

  1. The outer top of a floating box may not be higher than the outer top of any block or floated box generated by an element earlier in the source document.
  1. The outer top of an element's floating box may not be higher than the top of any line-box containing a box generated by an element earlier in the source document.

定义中没有例外。但例外恰恰是有的,那就垂直负边距。而所有的浏览器在实现时,浮动元素都会跟着垂直负边距移动,从而高于定义中的描述(具体参见CSS2.1 Issue 229)。为了修正标准,这句话应运而生了!不过糟糕的文法就实在不敢恭维...

好了,文章到这里也差不多了。欢迎吐槽我文中的错误和疏漏。最后祝大家国庆假期愉快~:)