【深入BFC】 关于CSS中float布局,清除浮动,和margin合并的原理解析,解开你心中的那些困惑!

BFC的通俗理解:

  Block Formatting Context(块级格式化上下文)是W3C CSS 2.1 规范中的一个概念,它决定了元素如何对其内容进行定位,以及与其他元素的关系和相互作用。

  简单来讲,我们可以把它理解为,我们在进行盒模型布局的时候,如果一个元素符合了成为BFC的条件,该元素成为一个隔离了的独立容器,元素内部元素会垂直的沿着其父元素的边框排列,和外部元素互不影响 。比如浮动元素会触发BFC,浮动元素内部的子元素主要受到该浮动元素的影响,而两个浮动元素之间是互不影响的。

  在CSS3 中,BFC 叫做Flow Root。在早期的ie中也有类似的概念haslayout IE6、7的很多布局产生的bug(如3px间隙、绝对定位的继承宽度)都可以通过触发hasLayout修复,比较推荐的方法为zoom:1与height:1%,不会破坏已有的样式,相信大家对它并不陌生。

  同样的以往集中在float、绝对定位、margin collaspe中的很多困惑,在理解了bfc后,都能够被我们一一解除 。


BFC规范中的定义:

w3c规范对BFC的解释:
  浮动元素和绝对定位元素,不是块级盒子的块容器 (如 inline-blocks, table-cells, 和 table-captions),以及设置了overflow属性(除了visible)的块级盒子 ,都会为他们的内容创建新的BFC(块级格式上下文)。
  在BFC中,盒子从顶端开始垂直地 一个接一个地排列,两个盒子之间的垂直的间隙是由他们的margin 值所决定的。在一个BFC中,两个相邻的块级盒子的垂直外边距会产生折叠。
  在BFC中,每一个盒子的左外边缘(margin-left)会触碰到容器的左边缘(border-left)(对于从右到左的格式来说,则触碰到右边缘)。

触发BFC的方法:

float 元素
position(absolute,fixed)
display (table-cell,table-caption,inline-block)
overflow 除了visible 以外的值(hidden,auto,scroll )
fieldset元素
早期IE的hasLayout会触发一个新的block formatting context

BFC的特性

  1. 边缘不和浮动元素重叠
  2. 不存在collapsing margins问题

  第一个特性特别有用,因为元素触发了BFC的话,就不会被float元素覆盖,当子元素全部浮动的时候也能够正确地包含了
  第二个margin不会叠加的特性,可以理解为两个处于普通流的盒子,会有margin叠加的问题,是因为他们属于相同的BFC,当他自身创建了一个新的BFC时,这个问题就不存在了

BFC的常见应用

1.通过边缘不和浮动元素重叠的特性,实现两栏结构。

  如果一个浮动元素后面跟着一个非浮动的元素,那么就会产生一个覆盖的现象,通过触发BFC来清除覆盖,很多自适应的两栏布局就是这么做的。
下面我们来看左边图片+右边信息的示例:

// CSS
div,p{margin:0;padding:0;}
.box {width:320px;}
.img {width: 80px;height: 80px;margin-right:10px;float:left;}
.info {background: #ff9;color: #404040;}

// HTML
<div class="box">
    <div class="img"><img src="http://wwc.taobaocdn.com/avatar/getAvatar.do?userNick=feihu2987&width=80&height=80&type=sns" width="80" height="80" alt="头像"></div>
    <p class="info">风吹亮雪花,吹白我们的头发,当初说一起闯天下,你们还记得吗?</p>
</div>

一般情况下它呈现出我们所乐意看到的样子:

但随着文字信息增多后,会变地非常的糟糕:

很明显,这是因为info类里面的文字受到了浮动元素的影响,但这并不是我们所期望的。此时我们可以为P元素的内容建立一个BFC,让其内容消除对外界浮动元素的影响。根据上文所知,只要给info元素添加overflow:hidden;即可为其内容建立新的BFC。当然你也可以通过其他方法来建立。其效果如下:

至此,我们可以抛弃计算设置左边边距,或是设置右边浮动再清除父元素的浮动这些弱爆的方式了。

demo地址: http://jsfiddle.net/k9u5x/

当然考虑到ie6 7 的兼容性,我们同时触发一下 haslayout就再好不过了,更多示例请看豆瓣小组和阅读中,很多地方使用了此方式:

http://www.douban.com/group/explore/culture

.channel-item .bd, .channel-item .block,.channel-item .block p, .mod .hd, .channel-group-rec li, .channel-group-rec li .info {
    overflow: hidden;
    zoom: 1;
}


2.清除元素内部浮动

只要把父元素设为BFC就可以清理子元素的浮动了,同样的因为IE6-7不支持BFC,因此要设置zoom:1来触发hasLayout 闭合浮动。

我们最常见的兼容用法就是在父元素上添加class clearfix ;

.clearfix {*zoom:1}
.clearfix:after{content:"\0020";display:block;height:0;clear:both;visibility:hidden}

 

3.解决合并外边距的问题

  在CSS当中,相邻的两个盒子(可能是兄弟关系也可能是父子关系)的外边距可以结合成一个单独的外边距。这种合并外边距的方式被称为折叠,并且因而所结合成的外边距称为折叠外边距。
  按照BFC的定义,只有同属于一个BFC时,两个元素才有可能发生垂直Margin的重叠,这个包括相邻元素,嵌套元素,只要他们之间没有线盒(其实就是非空内容),没有间隙(clearance,设置clear以闭合相关方向的浮动 ),没有padding和border将他们分隔开就会发生margin重叠。

  因此要解决margin重叠问题,只要让它们不在同一个BFC就行了,但是对于两个相邻元素来说,意义不大,没有必要给它们加个外壳,但是对于嵌套元素来说就很有必要了,只要把父元素设为BFC就可以了。这样子元素的margin就不会和父元素的margin发生折叠了。

折叠的结果:

两个相邻的外边距都是正数时,折叠结果是它们两者之间较大的值( 10px 20px 结果为20px )。
两个相邻的外边距都是负数时,折叠结果是两者绝对值较大的值( -10px -20px 结果是-20px )。
两个外边距一正一负时,折叠结果是两者的相加的和( 10px -20px 结果是10px )。
产生折叠的必备条件:margin必须是边缘毗邻的

而根据w3c规范,两个margin是垂直毗邻的必须满足以下条件:

元素的margin-top与其第一个常规文档流的子元素的margin-top
元素的margin-bottom与其下一个常规文档流的兄弟元素的margin-top
height为auto的元素的margin-bottom与其最后一个常规文档流的子元素的margin-bottom
高度为0并且最小高度也为0,不包含常规文档流的子元素,并且自身没有建立新的BFC的元素的margin-top和margin-bottom
只要触发Block Formatting Contexts的条件 或者是使用非空内容,空隙(clearance),padding和border将他们分隔开margin便不会折叠。

分析一:浮动和绝对定位不与任何元素产生 margin 折叠

原因:浮动元素和绝对定位元素不与其他盒子产生外边距折叠是因为元素会脱离当前的文档流,违反了上面所述的两个margin是邻接的条件同时,又因为浮动和绝对定位会使元素为它的内容创建新的BFC,因此该元素和子元素所处的BFC是不相同的,因此也不会产生margin的折叠。

分析二:inline-block元素与其兄弟元素、子元素和父元素的外边距都不会折叠(包括其父元素和子元素)

inline-block不符合w3c规范所说元素必须是块级盒子的条件,因为规范中又说明,块级盒子的display属性必须是以下三种之一:'block', 'list-item', 和 'table'。

分析三:设置有些属性(如float、position、display)会同时形成BFC与触发hasLayout,或者在>=IE7时也可以设置overflow同时搞定两者。

在IE7以下,只触发(形成)了其中一个,也最好同时触发(形成)另一个,以保证浏览器的兼容。
简单的触发方法:{overflow: hidden;zoom:1;}

 

如果感觉阅读此文后,有一定的启发和收获,劳驾您推荐下,在此谢过,欢迎留言交流。

 

posted @ 2014-01-24 13:24 花落红尘 阅读(...) 评论(...) 编辑 收藏