很多人初次接触float属性,可能都是看到或者使用float进行布局。但是float属性的本意并不是用于布局,要真正理解float属性,就要首先了解它被添加到CSS中的本来意图。

设计意图

当初添加float属性的本意是为了实现纸质媒体上常见的文字环绕图片效果

可是大多数情况,float都被用于布局,又是为什么呢?

因为在CSS3之前CSS中并没有专门用于布局的属性,所以floatposition等属性因为它们的一些特性就被「临时征用」用来布局了。

但是因为这些属性本来不是用于布局的,所以使用它们进行布局存在问题或者说需要注意的地方是很正常的,因为这本来就不是它们的本职工作嘛。

个人觉得,善于使用并且创造性地使用属性来完成当前看似无法完成的效果,然后在使用过程中发现并解决这个过程中出现的问题,最终设计并且添加专门的属性的过程是Web如此吸引人的原因以及推动Web不断向前发展的动力。

为了更好地使用float属性,并且尽量避免和处理好使用过程中遇到的问题,我们需要了解浮动元素的特性。

浮动元素的特性

浮动元素会脱离正常流,但是还会在布局中占有位置,同时生成一个和普通盒模型有差异的块级盒子。

浮动元素脱离了正常流,但是对其它元素还有影响

浮动元素会从正常流中脱离,正常流中的其他块级元素会当它不存在。

但是和同样脱离了正常流的绝对定位元素不同,浮动元素仍然在布局中占有位置。正是这种「灵魂出窍」的行为实现了文字环绕图片效果。

See the Pen float element is not in normal flow by xingzhi (@xingzhi) on CodePen.

浮动元素会生成块级盒子

无论是行内元素还是块级元素,浮动之后都会生成块级盒子。所以对浮动元素设置display: block是完全多余的。

浮动元素的宽度

替换元素浮动后的width属性值的确定和在正常流中没有区别,这里不再说明。

对于非替换元素,如果没有设置width属性,那么它的值会是auto,这和普通盒模型中是一样的。

只是widthauto的浮动非替换元素会收缩以适应内容宽度(shrink-to-fit),这和普通盒模型中扩展以充满包含块是相反的。

If ‘width’ is computed as ‘auto’, the used value is the “shrink-to-fit” width.

Calculation of the shrink-to-fit width is similar to calculating the width of a table cell using the automatic table layout algorithm. Roughly: calculate the preferred width by formatting the content without breaking lines other than where explicit line breaks occur, and also calculate the preferred minimum width, e.g., by trying all possible line breaks. CSS 2.1 does not define the exact algorithm. Thirdly, find the available width: in this case, this is the width of the containing block minus the used values of ‘margin-left’, ‘border-left-width’, ‘padding-left’, ‘padding-right’, ‘border-right-width’, ‘margin-right’, and the widths of any relevant scroll bars.

Then the shrink-to-fit width is: min(max(preferred minimum width, available width), preferred width).

浮动元素不会合并外边距

浮动元素和普通块级元素的一个重要区别就是不会合并外边距,无论相邻的元素是浮动元素还是普通的块级元素。

浮动规则

任何元素的位置都是相对于包含块而定的,浮动元素的包含块是块级父元素的内容区。关于包含块的更多内容可以参考 CSS包含块

CSS规范中,关于确定浮动元素的具体位置的完整规则比较繁琐,总结如下:

  1. 水平方向上,浮动元素会尽可能向左或者向右移动直到碰到包含块的边界或者另一个浮动元素的边界
  2. 除非比包含块宽,浮动元素的左右边界不可以超过包含块的左右边界
  3. 当水平方向上空间不足时,浮动元素会向下移动,在新的空间里按规则1移动
  4. 竖直方向上,浮动元素会尽可能向上移动,直到同时满足浮动元素的上边界恰好不超过浮动元素浮动之前所在的行框的上边界、在文档中位置更靠前的浮动元素或者块级元素的上边界以及包含块的上边界

清理浮动

默认情况下,浮动元素周围的元素的内容会围绕浮动元素。

当我们想改变这种默认行为时,就可以设置clear属性来强制某个元素的左侧(clear: left)、右侧(clear: right)或者两边(clear: both)不可以出现浮动元素。

高度塌陷

高度塌陷是指当包含块的高度小于浮动元素的高度时,浮动元素的下边界超出包含块的下边界的现象。

首先要强调的是高度塌陷是float元素脱离正常流带来的正常结果,也是必然结果

前面已经说过,浮动元素脱离正常流,正常流中的块级元素当它不存在。

具体到高度塌陷这里就是浮动元素的(包含块)父元素无视它,包含块的高度只由未浮动部分的高度决定,当包含块的内容的高度小于浮动元素的高度时,浮动元素脱离正常流的「狐狸尾巴」就露出来了,表现为高度塌陷。

知道了高度塌陷的原因,下面就来解决高度塌陷问题。

解决高度塌陷问题

从视觉效果上看,解决高度塌陷问题是为了包含块在高度上容纳浮动元素

而从CSS的可维护上看,解决高度塌陷问题是把浮动带来的问题封装在浮动元素的包含块范围内,让包含块外的其他元素不受影响。

浮动(包含块)父元素

CSS2.1规定,浮动元素会延伸,从而包含其所有后代浮动元素,所以浮动父元素就解决了高度塌陷问题。

这种方法简单兼容性好,但是扩散了浮动的影响,不建议使用。

在父元素的末尾添加块级元素并且在该元素上设置clear属性

这种方法同样具有简单兼容性好的优点,但是因为它为表现引入了无用的元素,和语义化标签原则和内容与表现分离原则相违背,现在已经没有人采用。

需要特别指出的是,添加的元素必须是块级元素,行内元素需要设置display属性让其生成块级盒子,因为clear属性只对块级盒子起作用。这里的强调是为了说明下一个方法中after伪元素设置display: block的原因。

clearfix

这个方法本质上和上一个方法是一致的,只是很巧妙地使用了包含块的after伪元素,从而避免了添加无用的标签。

1
2
3
4
5
.clearfix:after {
  content: '',
  display: block;
  clear: both;
}

这是目前使用最广泛的方法,推荐使用。而且compass提供了cleaxfix mixin,可以很方便地使用。

这个方法在IE7-中存在兼容性问题,不过谁还需要兼容IE7就只能表示同情了,毕竟现在IE8都已经像当年的IE6一样被嫌弃了。

在父元素上设置overflow属性值为auto或hidden

这个方法同样具有简单兼容性好的优点。不过因为overflow属性并不是为了解决这个问题而设计的,这个方法缺点也很明显。

当包含块设置了height属性或/和包含块中有元素设置了负的margin或定位时,有可能出现内容被隐藏(overflow: hidden)或者出现滚动条(overflow: auto)的问题。

使用JavaScirpt设置float属性

float是JavaScript的保留字,因此获取或者设置float属性不能使用float,而是使用styleFloat(IE8-)或者cssFloat(其他浏览器)。

但愿读者永远也用不到这点知识,因为使用JavaScript改变样式推荐的方法是切换元素的类,而不是直接操作元素的单个属性。