深入理解浮动与清除浮动,玩转「float」属性
有些属性就像姑娘一样让人爱的深沉,又像敌人一样让人肉痛不已,float即是如此。
什么是浮动
浮动是脱离文档的普通流而存在的(可以认为是漂浮在普通文档流上),可以左右浮动,直到它的外边缘遇到
- 包含框
- 另一个浮动框
浮动的影响
因为浮动框不在文档普通流中,所以在布局的时候,文档中的普通流元素就会表现得和浮动框不存在一样, 当浮动框高度超出了包含框的时候,也就会出现包含框不会自动伸高来闭合浮动元素;
浮动元素脱离了普通文档流,使得包含框的高度发生了变化,包含框内部由于不存在其他普通元素,就会表现出高度为0,发生高度塌陷,在实际的布局中,这不是我们所希望的,因此我们需要闭合浮动元素,使得其包含框表现出正常的高度。
清除浮动的方法
1. 添加额外标签
html代码
<div class="warp">
<div class="main left">main</div>
<div class="side left">side</div>
<div style="clear: both;"></div>
</div>
css代码
* {margin:0px; padding:0}
.wrap {
border: 1px solid blue;
width: 400px;
margin: 30px auto 5px;
background: #F5F5F5;
}
.main {
height: 60px;
width: 50%;
background: #FFE3D7;
}
.side {
width: 20%;
background: lightblue;
}
.left { float: left; }
效果对比
理解: 可以看到,没有添加<div class="clear:both"></div>
的<div>
父元素并没有被撑开,原因是main和side都被设置了浮动,脱离了文档普通流,更像是悬浮在父元素之上;而清除了浮动之后就撑开了。
优劣分析: 虽通俗易懂,容易掌握,但这种方法会产生很多额外的空标签,有违结构与表现的分离,在后期的维护中将会是噩梦,不建议使用这种方式。
2. 使用br和自身的html属性
或许你并不知道,<br>
标签具有clear = "all | left | right | none"
属性
html代码
<div class="wrap">
<div class="main left">main</div>
<div class="side left">side</div>
<br clear="all" />
</div>
css代码
* {margin:0px; padding:0}
.wrap {
border: 1px solid blue;
width: 400px;
margin: 30px auto 5px;
background: #F5F5F5;
}
.main {
height: 60px;
width: 50%;
background: #FFE3D7;
}
.side {
width: 20%;
background: lightblue;
}
.left { float: left; }
效果展示
优劣分析: 该方法与上例方法类似,更简洁一些,但是结构与表现分离原则依旧存在,依旧不易维护,不推荐。
3. 父元素设置 overflow: hidden
注: 在IE6中由于hack还需要触发 hasLayout ,例如 *zoom:1;
html代码
<div class="wrap clearfix">
<div class="main left">main</div>
<div class="side left">side</div>
</div>
css代码
* {margin:0px; padding:0}
.clearfix {
overflow: hidden;
}
.clearfix { *zoom: 1; } /* IE6 */
.wrap {
border: 1px solid blue;
width: 400px;
margin: 30px auto 5px;
background: #F5F5F5;
}
.main {
height: 60px;
width: 50%;
background: #FFE3D7;
}
.side {
width: 20%;
background: lightblue;
}
.left { float: left; }
效果展示
优劣分析: 不存在结构和语义化问题,代码量极少,但因overflow: hidden
本身的问题,如果内容较多,就会被自动隐藏。无法显示溢出的元素,既然有缺陷,还是不要用了
4. 父元素设置 overflow: auto
注: 在IE6中同样由于hack还需要触发 hasLayout ,例如 *zoom:1;
css代码
.clearfix {
overflow: auto;
}
.clearfix { *zoom: 1; } /* IE6 */
// 其他同上
效果展示:
优劣分析: 不存在结构和语义化问题,代码量极少,也解决了内容较多时被隐藏现象,但还是会出现小问题,多个嵌套后,firefox某些情况会造成内容全选;IE中 mouseover 造成宽度改变时会出现最外层模块有滚动条等,firefox早期版本会无故产生focus等。抱怨hack也没用,这些都是痛啊。至少这种方式还算能用一用
5. 父元素也设置浮动
html代码
<div class="wrap">
<div class="main left">main</div>
<div class="side left">side</div>
</div>
css代码
.wrap {
border: 1px solid blue;
width: 400px;
margin: 30px auto 5px;
background: #F5F5F5;
float: left;
}
效果展示
优劣分析: 不存在结构和语义化问题,代码量极少,但从效果中可以看出来,父元素设置了左浮动,父元素的布局受到了影响,因为子元素而影响父元素,得不偿失,我们也没办法一直设置到根元素,不采取。
6. 设置父元素为 display: table
html代码
<div class="wrap">
<div class="main left">main</div>
<div class="side left">side</div>
</div>
css代码
.wrap {
border: 1px solid blue;
width: 400px;
margin: 30px auto 5px;
background: #F5F5F5;
display: table;
}
效果展示
优劣分析: 结构语义化完全正确,代码量极少;但引入display属性后,盒模型属性已经改变,由此造成的一系列问题,得不偿失,也不太适用
7. 使用 :after
伪元素( 最佳实践 )
html代码
<div class="wrap clearfix">
<div class="main left">main</div>
<div class="side left">side</div>
</div>
css代码
* {margin:0px; padding:0; background-color: #9c663e;}
.clearfix:before, .clearfix:after {
content: ".";
display: block;
height: 0;
visibility: hidden;
}
.clearfix:after {clear: both;}
.clearfix {*zoom: 1;} /* IE < 8 */
.wrap {
border: 1px solid blue;
width: 400px;
margin: 30px auto 5px;
background: #F5F5F5;
}
效果展示
8. 精益求精
(1) 相对于空标签闭合浮动的方法代码似乎还是有些冗余,通过查询发现Unicode字符里有一个“零宽度空格”,也就是U+200B ,这个字符本身是不可见的,所以我们完全可以省略掉 visibility:hidden了
html代码
<div class="wrap clearfix">
<div class="main left">main</div>
<div class="side left">side</div>
</div>
css代码
.clearfix:after {
content:"\200B";
display:block;
height:0;
clear:both;
}
.clearfix {zoom: 1;} /* IE < 8 */
效果展示
(2) 由Nicolas Gallagher 大湿提出来的,原文:A new micro clearfix hack,该方法也不存在firefox中空隙的问题。
html代码
<div class="wrap clearfix">
<div class="main left">main</div>
<div class="side left">side</div>
</div>
css代码
.clearfix:before, .clearfix:after {
content:"";
display:table;
}
.clearfix:after { clear:both; } /* For IE 6/7 (trigger hasLayout) */
.clearfix { zoom:1; }
需要注意的是:
上面的方法用到了 :before伪元素,很多人对这个有些迷惑,到底我什么时候需要用before呢?为什么方案一没有呢?其实它是用来处理margin边距重叠的,由于 内部元素 float 创建了BFC,导致内部元素的margin-top和 上一个盒子的margin-bottom 发生叠加。如果这不是你所希望的,那么就可以加上before,如果只是单纯的闭合浮动,after就够了!
至此,介绍了这么多使用方法,是不是对该属性还很困惑?如果你只想知道怎么使用,到这就可以关闭网页了,但如果你想更深入的理解,请继续往下看
浮动的原理
通过对比,我们不难发现,其实以上列举的方法,无非有两类:
- 通过在浮动元素的末尾添加一个空元素,设置
clear:both
属性,after
伪元素其实也是通过content
在元素的后面生成了内容为一个点的块级元素; - 通过设置父元素
overflow
或者display:table
属性来闭合浮动;
我们来探讨一下这里面的原理。
在CSS2.1
里面有一个很重要的概念,但是国内的技术博客介绍到的比较少,那就是 Block formatting contexts
(块级格式化上下文),以下简称 BFC
;
CSS3
里面对这个规范做了改动,称之为:flow root
,并且对触发条件进行了进一步说明。
那么如何触发BFC呢?
-
float
除了none
以外的值 -
overflow
除了visible 以外的值(hidden,auto,scroll )
display (table-cell,table-caption,inline-block)
position(absolute,fixed)
-
fieldset
元素
需要注意的是,display:table
本身并不会创建BFC
,但是它会产生匿名框(anonymous boxes)
,而匿名框中的display:table-cell
可以创建新的BFC
,换句话说,触发块级格式化上下文的是匿名框,而不是display:table
。所以通过display:table
和display:table-cell
创建的BFC
效果是不一样的。
BFC的特性:
1. 块级格式化上下文会阻止外边距叠加
当两个相邻的块框在同一个块级格式化上下文中时,它们之间垂直方向的外边距会发生叠加。换句话说,如果这两个相邻的块框不属于同一个块级格式化上下文,那么它们的外边距就不会叠加。
2. 块级格式化上下文不会重叠浮动元素
根据规定,一个块级格式化上下文的边框不能和它里面的元素的外边距重叠。这就意味着浏览器将会给块级格式化上下文创建隐式的外边距来阻止它和浮动元 素的外边距叠加。由于这个原因,当给一个挨着浮动的块级格式化上下文添加负的外边距时将会不起作用
3. 块级格式化上下文通常可以包含浮动
详见: W3C CSS2.1 - 10.6.7 'Auto' heights for block formatting context roots
通俗地来说:创建了 BFC的元素就是一个独立的盒子,里面的子元素不会在布局上影响外面的元素,反之亦然,同时BFC任然属于文档中的普通流
至此,您或许明白了为什么 overflow:hidden
或者auto
可以闭合浮动了,真是因为父元素创建了新的BFC。