首页 > 代码库 > BFC与haslayout

BFC与haslayout

  前几天学习的时候看到了BFC这个词,好奇之余在网上查了相关文章,终于搞明白了BCF到底是什么东西。发现自己布局时早已使用过其特性解决问题,只是不知道这个具体概念以及内在原理。下面就谈谈我对BFC的理解吧。

  BFC是(Block Formatting Context)的缩写,中文翻译过来是块格式化上下文,是W3C CSS 2.1 规范中的一个概念,在CSS3中,BFC  叫做  Flow Root。其实从其字面意思上我们已经可以看出些端倪:context——上下文;其实我自己理解的就是提供参照,那么为什么东西提供参照呢?当然是包含在创建了BFC元素内部的子元素了。也就是说,BFC为其包含的子元素创建了一个布局环境,不同BFC之间互不影响,比如浮动元素会形成 BFC,浮动元素内部子元素的主要受该浮动元素影响,两个浮动元素之间是互不影响的。下面是W3C规范的创建了BFC的元素对其子元素的行为规则:

  1.In a block formatting context, boxes are laid out one after the other, vertically, beginning at the top of a containing block. The vertical distance between two sibling boxes is determined by the ‘margin‘ properties. Vertical margins between adjacent block-level boxes in a block formatting context collapse.

  【元素的子元素会一个接一个地放置。垂直方向上他们的起点是一个包含块的顶部,两个相邻的元素之间的垂直距离取决于‘margin‘ 特性。在BFC中相邻的块级元素的垂直边距会折叠。】 

  2.In a block formatting context, each box‘s left outer edge touches the left edge of the containing block (for right-to-left formatting, right edges touch). This is true even in the presence of floats (although a box‘s line boxes may shrink due to the floats), unless the box establishes a new block formatting context (in which case the box itself may become narrower due to the floats).

  【元素的子元素中,每一个子元素左外边与包含块的左边相接触(对于从右到左的格式化,右外边接触右边),即使存在浮动也是如此(尽管一个子元素的内容区域会由于浮动而压缩),除非这个子元素也创建了一个新的BFC,如它自身也是一个浮动。】

  英文部分是W3C原文,下面是中文翻译。翻译难免有偏差,把原文粘过来方便理解。下面简单解释下这两段话:

  1.第一段话前面没什么好说的,就是说BFC元素内部元素正常情况下会按照常规的文档流的形式布局,有意思的是第二句话“在BFC中相邻的块级元素的垂直边距会折叠”。好了,我们来看看这个垂直边距重叠的具体效果,代码如下:

<!DOCTYPE html>

<html>
<head>
    <title></title>
    <style type="text/css">
      .father{
        width: 700px;
        height: 500px;
        font-size: 100px;
        font-weight: bold;
        background-color: black;
         }
      .child1{
        width: 300px;
        height: 300px;
        background-color: red;
        margin-top: 50px;
        margin-bottom: 50px;
          }
      .child2{
        width: 500px;
        height: 300px;
        background-color: green;
        border: 5px solid black;
        margin-top: 50px;
        margin-bottom: 50px;
          }
      </style>
</head>
<body>
  <div class="father">
    <div class="child1">1</div>
    <div class="child2">2</div>
  </div>
</body>
</html>

我们会看到下图的效果:

            

可以看到,这里child1和child2同时设置了50px的margin-top和margin-bottom,但实际上child1和child2之间垂直距离总共只有50px,也就是说发生了垂直边距的重叠。而更过分的是,我们原本想让child1的顶部与father元素(背景为black)有50px的边距,但实际效果却是child1的margin将父元素顶部的外边距撑开了50px,而这个也是同一个BFC外边距会重叠的原因——因为原文是相邻元素,这个相邻包括相邻子元素与嵌套元素。换句话说,属于一个BFC时,两个元素才有可能发生垂直Margin的重叠,这个包括相邻元素,嵌套元素,只要他们之间没有阻挡(例如边框,非空内容,padding等)就会发生margin重叠。因此要解决margin重叠问题,只要让它们不在同一个BFC就行了,但是对于两个相邻元素来说,意义不大,没有必要给它们加个外壳,但是对于嵌套元素来说就很有必要了,只要把父元素设为BFC就可以了。这样子元素的margin就不会和父元素的margin发生重叠了。具体到上面的例子,如果我们给father元素设置border,padding,或者是除了值"visible"以外的overflow,都可以防止容器内的第一个子元素margin-top将容器上边距撑开的问题(border,padding使其不相邻,而overflow则是创建了新的BFC)。

  2.第二段话其实也没什么好解释的,意思就是说如果一个元素符合了成为 BFC 的条件,该元素内部元素的布局和定位就和外部元素互不影响(除非内部的盒子建立了新的  BFC), 是一个隔离了的独立容器。每个容器内的子元素的左边外边都与其自身的包含块的左边接触。

好了,简单解释了一下标准,下面就看看如何形成BFC,以及BFC的用途。

  满足下列条件之一均能形成一个BFC环境:    

      1)  float 的值不为 none;
      2)  overflow 的值不为 visible;
      3)  display 的值为  table-cell、table-caption 和 inline-block 之一;
      4)  position 的值不为  static 或  relative 中的任何一个;

 BFC一般有以下几个作用:

   1)包含浮动元素:

      我们知道,如果子元素浮动,父元素的高度会塌陷,但是如果让父元素形成BFC就能使其根据子元素的高度自适应。还是上面的代码,将father的宽高去掉,加上border:1px solid blue;并且给child1和child2都设置float:left。我们将会看到:

      

     (father元素的高度坍塌,上下border重叠)

     现在给father加上overflow:hidden:

      

      (father元素设置了overflow,创建了BFC,高度得以重新计算)

    2)利用BFC防止嵌套元素垂直边距重叠。这个就是前面的展示的例子。

    3)利用BFC防止被float元素覆盖:

      float元素会无视兄弟元素的存在,覆盖在兄弟元素的上面,我们可以给兄弟元素创建新的BFC来防止被覆盖:

      给child1设置float:left:

        

      (child2未创建BFC,被浮动元素child1覆盖)

      同时给child2设置overflow:hidden;

        

        (child2创建了BCF)

至此,对BFC已经有比较清晰的认识了,不过不是所有浏览器都支持BFC规则的,比如奇葩浏览器IE6、IE7。在IE6、7中像上文中这样利用BCF特性解决问题有时候不会得到预期的效果。不过好在在IE中有类似的haslayout属性:当元素的hasLayout属性值为false的时候,元素的尺寸和位置由最近拥有布局的祖先元素控制。当元素的hasLayout属性值为true的时候会达到和BFC类似的效果,元素负责本身及其子元素的尺寸设置和定位。IE这个属性的初衷是为了保持浏览器的性能,因为在理想情况下,所有元素都控制自己的尺寸和定位。但是,这在IE中会导致很大的性能问题。因此,IE开发团队决定只将布局应用于实际需要它的那些元素,这样就可以充分地减少性能开销。默认情况下,IE中下列标签的haslayout是true:

  

<html>, <body>

 

<table>, <tr>, <th>, <td>

 

<img>

 

<hr>

 

<input>, <button>, <select>, <textarea>, <fieldset>, <legend>

 

<iframe>, <embed>, <object>, <applet>

 

<marquee>

 

 也即是说,默认情况下,这些标签可以控制决定其自身内部子元素的布局。

 

    此外,在IE6、7中,做如下设置将使元素的haslayout属性为true:

        1)position: absolute

        2)float: left|right

       3)display: inline-block

       4)width: 除 “auto” 外的任意值

       5)height: 除 “auto” 外的任意值

       6)zoom: 除 “normal” 外的任意值

       7)writing-mode: tb-rl

 

 在IE7中使用overflow: hidden|scroll|auto 也可以使hasLayout为true。

PS:具有“layout” 的元素如果同时 display: inline ,那么它的行为就和标准中所说的 inline-block很类似了:在段落中和普通文字一样在水平方向和连续排列,受 vertical-align影响,并且大小可以根据内容自适应调整。

 

总结:其实本文中提到的BCF的很多使用场合在此前的布局中都经常使用,只是以前一直仅仅将其当做一个技巧使用,现在总算明白其原理所在。知其然必知其所以然,才能走的更远。

                           

BFC与haslayout