float 的本质
float 属性是一个年代非常久远的属性。float 属性设计的目的是什么呢?一句话: 浮动的本质就是为了实现文字环绕效果。而这种文字环绕主要指的就是文字环绕图片显示的效果。
float 的特质
包裹性
所谓“包裹性”,由“包裹” 和“自适应性” 两部分组成。
(1)包裹。假设浮动元素父元素宽度为200px,浮动元素子元素是一个128px 宽度的图片,则此时浮动元素宽度表现为“包裹”,就是里面的图片宽度为128px,代码如下:
1 | .father { width: 200px; } |
1 | <div class="father"> |
(2)自适应性。如果浮动元素的子元素不只是一张128px 宽度的图片,还有一大波普通的文字,例如:
1 | <div class="father"> |
则此时浮动元素宽度就自适应父元素的200px 宽度,最终的宽度表现也是200px。
当然,要想最大宽度自适应父元素宽度,一定是在浮动元素的“首选最小宽度” 比父元素的跨度要小的前提下,比方说上面示意的文字全是一连串超长的英文字母,则浮动元素的宽度显然就不是200px 了。
块状化并格式化上下文
块状化的意思是,元素一旦float 的属性值不为none,则其display 计算值就是block 或table。举个例子,打开浏览器控制台,输入如下JavaScript 代码:
1 | var span = document.createElement('span') |
结果如图
因此没有任何理由出现下面的组合:
1 | span { |
也不要指望使用text-align 属性控制浮动元素的左右对齐,因为text-align 对块级元素是无效的。
破坏文档流
float 特性的精髓 – “破坏文档流”,这可以说是float 属性的万恶之源,但也是float 属性的立命之本,是其作用机制之所在。
没有任何margin 合并
float 的作用机制
float 属性有个著名的特性表现,就是会让父元素的高度塌陷,大多数场景下,这种特性会影响“正常的” 布局,这里我特意把“正常的” 三个字加了引号,因为站在CSS 属性的角度讲,我们希望的结果反而是一种不正常,高度塌陷才是正常。
float 属性的原本作用“只是为了实现文字环绕效果”,而float 属性让父元素塌陷的原因就是为了实现文字环绕效果。
然而,“高度塌陷” 只是让跟随的内容可以和浮动元素在一个水平线上,但这只是实现“环绕效果” 的条件之一,要想实现真正的“环绕效果”,就需要另外一个平时大家不太在意的特性,那就是“行框盒子和浮动元素的不可重叠性”,也就是“行框盒子如果和浮动元素的垂直高度有重叠,则行框盒子在正常定位状态下只会跟随浮动元素,而不会发生重叠”。
注意,这里说的是“行框盒子”,也就是每行内联元素所在的那个盒子,而非外部的块状盒子。实际上,由于浮动元素的塌陷,块状盒子是和图片完全重叠的。
float 更深入的作用机制
首先,我们需要了解两个和float 相关的术语,一是“浮动锚点”(float anchor),二是“浮动参考”(float reference)。
- 浮动锚点是float 元素所在的“流” 中的一个点,这个点本身并不浮动,就表现而言更像一个没有margin、border 和padding 的空的内联元素。
- 浮动参考指的是浮动元素对齐参考的实体。
在CSS 世界中,float 元素的“浮动参考” 是“行框盒子”,也就是float 元素在当前“行框盒子” 内定位。再强调一遍,是“行框盒子”,不是外面的包含块盒子之类的东西,因为CSS 浮动设计的初衷仅仅是实现文字环绕效果。
float 与流体布局
我们可以利用float 破坏CSS 正常流的特性,实现俩栏或多栏的自适应布局。
1 | .father { overflow: hidden; } |
1 | <div class="father"> |
原理很简单,animal 元素没有浮动,也没有设置宽度,因此,流动性保持很好,设置maring-left/border-left/padding-left 都可以自动改变content box 的尺寸,继而实现了宽度自适应布局效果。
多栏布局
1 | .prev { float: left; } |
1 | <div class="box"> |
也就是说,title 所在的<h3>
标题元素直接左右margin, 借助流体特性,保证不会和两个文字连接重叠。
float 天然克星clear
什么是clear 属性
CSS 有一个专门用来处理float 属性带来的高度塌陷等问题的属性,这个属性就是clear。其语法如下:
1 | clear: none | left | right | both |
官方对clear 属性的解释是:“元素盒子的边不能和前面的浮动元素相邻。”
就是设置了clear 属性的元素自身如何如何,而不是让float 元素如何如何。因为,我对clear 属性的理解是下面这样的。
- none:默认值,左右浮动来就来。
- left:左侧抗浮动。
- right:右侧抗浮动。
- both: 两侧抗浮动。
凡是clear:left 或clear:right 起作用的地方,一定可以使用clear:both 替换。
举个例子,假设容器宽度足够,有10 个<li>
元素,设置了如下CSS 代码:
1 | li { |
也就是说,第三个<li>
设置了clear:both,请问表现是怎样的?或者这么说吧:列表最后是1 行显示、2 行显示,还是3 行显示呢?
我们很容易被both 这个单词误导,因为其字面意思是“同时”,所以很多人会认为是3 行,但实际上只会显示2 行,如下图所示:
原因在于,clear 属性是让自身不能和前面的浮动元素相邻,注意这里“前面的” 3 个字,也就是clear 属性对“后面的” 浮动元素是不闻不问的,因此才2 行显示而不是3 行。
成事不足败事有余的clear
clear 属性只有块级元素才有效的,而::after 等伪元素默认都是内联水平,这就是借助伪元素清除浮动影响时需要设置display 属性值的原因。
1 | .clear:after { |
然而利用伪元素或直接使用下面HTML,有时候也会产生一些意想不到的问题:
1 | <div class="clear:both"></div> |
实例代码如下:
1 | <div class="father"> |
下图就是clear:both 导致自适应布局错位示意
由于clear:both 的作用本质是让自己不和float 元素在一行显示,并不是真正意义上的清除浮动,因此float 元素一些不好的特性依然存在,于是,会有类似下面的现象。
(1)如果clear:both 元素前面的元素就是float 元素,则clear:both 元素的margin-top 负值即使设成-9999px,也不见任何效果。
(2)clear:both 后面的元素依旧可能会发生文字环绕的现象。举个例子,如下HTML 和CSS:
1 | <div class="father"> |
1 | .father:after { |
虽然.father 父元素的最后设置了clear:both 来阻止浮动对后面元素的影响,但是最后结果错位依然发生了,如下图所示。
参考文档
1.《CSS 世界》 张鑫旭 6.1 魔鬼属性float
2.CSS 深入理解之float 浮动