深入理解BFC和IFC

1. 为什么会有BFC和IFC

  首先要先了解两个概念:Box和formatting context;
  Box:CSS渲染的时候是以Box作为渲染的基本单位。Box的类型由元素的类型和display属性决定,box的类型分为block-level box 和inline-level box(不包括css3的时候)。不同类型的box参与不同类型的formatting context布局。

  Block-level elements are those elements of the source document that are formatted visually as blocks (e.g., paragraphs). The following values of the 'display' property make an element block-level: 'block', 'list-item', and 'table'.

  Block-level boxes are boxes that participate in a block formatting context. 

  block-level box:形式上表现为块(此处和块元素的区别主要是不包含块元素里的non-replaced inline blocks(这里一般指canvas,svg,input元素等) and non-replaced table cells)或者display 属性为 block, list-item, table 的元素,会生成 block-level box。并且参与 block fomatting context; 
  inline-level box:display 属性为 inline, inline-block, inline-table 的元素,会生成 inline-level box。并且参与 inline formatting context;
  run-in box: css3 中才有,即GFC,FFC  
  Formatting context:每个渲染区域用formatting context表示。它决定了其子元素将如何定位,以及和其他元素的关系和相互作用
       BFC和IFC则可以理解为不同渲染区域遵循的不同规则。

2. 什么是BFC和IFC

  • BFC

  1. 什么时候会产生BFC:

  1. float is not none
  2. position is absolute & fixed
  3. display is table-cell,table-caption,inline-block,flex,inline-flex
  4. overflow is not visible
  5. block-level box

  2. 特性:

o   内部的Box会在垂直方向,一个接一个地放置。
o   Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠每个元素的margin box的左边,与包含块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此。
o   BFC的区域不会与float box重叠
o   BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
o   计算BFC的高度时,浮动元素也参与计算。  

  3. 作用:

    通常会用BFC来解释以下2个问题:
    1. margin collapse:上下margin会合并(这里不过多阐述,可参考链接[3])
    2. contain float: 
    其中的一种解决方法:overflow:hidden ,创建新的BFC,可以包含float元素,父元素就有高度值了

    此处阐述一下自己的理解:如果父元素只包含浮动元素,因为浮动元素是不包含在正常流的,浮动孩子将会脱离页面的常规流,因此父元素相当于不包含任何元素,高度为0。

    而BFC里面有两条规则:
    1、BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。
    2、计算BFC的高度时,浮动元素也参与计算
    overflow:hidden会产生新的BFC,子元素的被包含在父元素的BFC中,因此父元素不受子元素影响,恢复常规布局,遵循BFC规则,同时计算高度时,父元素的高度会将浮动元素算进来,因此父元素有了高度。

    不过这里要阐述一件事就是高度塌陷只出现在父元素包含且只包含浮动元素的时候:

    看图:在父元素没有明确定义height的时候,如果只包含浮动元素,父元素会出现高度塌陷的问题。(其中的解决方法主要有添加伪元素clear:both,设置父元素height,设置父元素overflow:hidden等方式)

下图:当父元素内部有非float元素时(clear:both的原理)
  
  • IFC
  IFC也是一种布局规则,inline元素和inline-block符合IFC的布局规则。在IFC布局中重点关注一下line box。
  (补充一下:IFC的生成条件是元素满足inline-level box)

       在IFC中,内联元素在水平方向上一个接一个的排布,其中,容器之间水平方向上的margin,padding,border方向上是好使的。他们垂直方向上有很多种对其方式,比如居底部或顶端对齐,或者基线对齐。他们对齐完了之后形成的这个四方块儿区域,叫做一个line box(行框)。

  一个line box的宽度由包含它的元素的宽度和包含它的元素里面有没有float元素来决定,而高度由内部元素中实际高度最高的元素而计算出来。

  line box的高度是足够高来包含他内部的容器们的,也可能比它包含的容器们都高(比如在基线对齐的时候),当他包含的内部容器的高度小于line box的高度的时候,内部容器的垂直位置由自己的vertical这个属性来确定。当内部的容器盒子太多了一个line box装不下来,他们折行之后会变成两个或者多个line box, line box们相互之间垂直方向不能分离,不能重叠。

  一般来说,line box的左边缘挨着包含它的元素的左边缘,并且右边缘挨着包含它的元素的右边缘,浮动元素会在包含他们的元素的边缘和line box的边缘之间,所以虽然在同一个IFC下的line box们通常拥有相同的宽度(就是包含他们的容器的宽度),但是也会因为浮动元素的捣乱,导致line box们的可用宽度产生了变化不一样了。在同一个Ifc下的line box们的高度也会不一样(比如说,一个line box里有个比较大的image,他就高了)。

  如果一个line box 里的内联元素们的宽度总和小于这个line box的宽度,那么他们在这个line box里的水平方向的排布方式由 text-align这个属性来决定,如果这个属性被设置成了“justify”,可以使这些盒子在剩余空间内拉伸(除了inline-table 和 inline-block的元素)

  在一个line box中,当他包含的内部容器的高度小于line box的高度的时候,内部容器的垂直位置由自己的vertical这个属性来确定。那么,我们设想一下,如果手动创建一个IFC的环境,让line box的高度是包含块的高度的100%,让line box内部的元素使用vertical-align:middle,就可以实现垂直居中。

  一个line box的高度由内部元素中实际高度最高的元素而计算出来。所以,我们在line box中插入一个高度100%的inline-block元素。则会把整个line box撑高直到包含块的100%.
  当内联元素的宽度超过了line box的宽度,那么它会折行分裂成了几个line box,如果这个元素里面的内容不可以折行,例如只有一个字,或者white-space设置了nowrap/pre。那么内联元素会溢出line box。
  当一个内联元素分裂时,分裂处的 margins, borders 和 padding不会有任何视觉效果(或者其他任何分裂,只要是有多个line box)。
  line box 的生存条件是在IFC中并且包含inline-level元素,如果line box里没有文本,空白,换行符,内联元素,也没有其他的存在IFC环境中的元素,(如inline-block,inline- table,images等),将会被视为零高度,也将会被视为没有意义。
  补充:在IFC的环境中,是不能存在block-level元素的,如果将block-level元素插入到IFC中,那么此IFC将会被破坏掉, 而block-level元素前的元素和block-level元素后的元素将会各自自动产生一个匿名容器其包围,这个匿名的容器内部环境将是一个新的 IFC。


  此处补充一些自己对于IFC高度计算的理解:主要是line-height和height

  IFC中height的计算方式:line height表示高度

  *line-height如果没有显式声明,那么继承父元素的line-height;如果显示声明了,则用声明后的高度(vertical-align的时候参考的边界值)

  css中的line-height表示line space(行间差值) + font-size

看下图:

 

可以得到:1.height对inline元素并不起作用 2.只有设置了line-height的inline元素才会参与vertical-align的排列规则(这里个人分析是因为只有设置了高度,vertical-align时才能计算到边界值,从而对其进行排列,否则默认都是baseline规则)

此处可以再看一个例子:

 

 这个地方设置第二个元素的line-height是80,而父元素的line-height是100,vertical-align默认是相对于父元素的排列。首先中线的位置是一半,所以(100/2)-(80/2)=10,第二个元素下移了10px。
更多关于vertical-align的知识可以参考[6],[7]。
 
参考文献:
posted @ 2016-12-26 18:40  兔昵  阅读(2697)  评论(4编辑  收藏  举报