CSS的两种格式化上下文:BFC和IFC

CSS的两种格式化上下文

  文章包含很多个人理解,如果错误,欢迎指出~

  在看本文之前,你要对CSS的盒子模型,Block-Level元素,Inline-Level元素有所了解,具体可参考CSS的盒子模型、三种元素类型

  本文具体解读了CSS针对block-level元素和inline-level元素设计的两种格式化上下文:BFC(Block Formatting Context)和IFC(Inline Formatting Context),它们规定了block-level元素和inline-level元素在对应格式化上下文中的渲染规则。了解上述内容是清楚理解网页排版的基础,当然在此基础上,还要进一步掌握CSS的几种定位方法,浮动等内容,才能理解和设计更加复杂的网页布局。

格式化上下文

  格式化上下文即Formatting context,它是指页面上的一个局部独立渲染区域,根据Formatting context中包含的是元素类型的不同,分为块级格式上下文BFC和行内格式化上下文IFC,不同的格式化上下文对应着不同的渲染规则,来告诉页面多个Block-Level元素或是多个Inline-Level元素在页面中该如何布局。需要注意,在BFC中只会包含Block-Level元素,同样的,在IFC中只会包含Inline-Level元素。

块级格式化上下文BFC(Block Formatting context)

  触发BCF的方法,下面这些元素都会产生一个新的BFC

  1. 根元素;
  2. float属性不为none;
  3. position为absolute或fixed;
  4. display为inline-block, table-cell, table-caption, flex, inline-flex;
  5. overflow不为visible;

  BFC包括如下特性

  1. BFC内部的Block-Level元素会在垂直方向,一个接一个地放置;

  2. 两个相邻的处于同个BFC的Block-Level元素的margin会发生重叠;这里包括相邻的兄弟元素和嵌套元素;发生重叠的具体条件,和重叠的方式,请看http://www.cnblogs.com/ningyn0712/p/5369024.html

  3. BFC内部的Block-Level元素的left outer edge与它的container Box的left inner edge重合;

  4. BFC不会与它同级的float元素发生重叠;

      利用这个特性可以很方便的实现动态两栏的结构。对于处于同一个BFC中Block-Level元素和一个float元素,它们是会发生重叠的,如Example2所示;但如果我们将Block-Level元素触发成一个新的BFC,这个Block-Level元素就会自动通过缩小box宽度避免float元素重叠,如Example 3所示;但是实验发现,通过设置overflow实现的BCF不能完全避免重叠,这个Block-Level元素的margin区域还是会与float元素发生重叠,如Example 4所示;希望有人可以帮忙解释原因

    Example 2: 在id为container-div的div标签中放置一个id为th1-div的浮动div标签和一个id为th2-div的普通标签;

     <style type="text/css">
         #container-div
         {
             background: cornflowerblue;
             height: 200px;
         }
         #th1-div
         {
             float: left;
             height: 100px;
              100px;
             background: red;
         }
         #th2-div
         {
             height: 100px;
             background: orange;
         }
     </style>
    
     <div id="container-div">
         <div id="th1-div">
         </div>
         <div id="th2-div">
         </div>
     </div>
    

**Example 3** :在id为container-div的div标签中放置一个id为th1-div的浮动div标签和一个id为th2-div的普通标签;通过设置#th2-div的overflow: hidden;来触发#th2-div的BFC,以达到#th1-div与#th2-div不重叠的目的;

	<style type="text/css">
	    #container-div
	    {
	        background: cornflowerblue;
	        height: 200px;
	    }
	    #th1-div
	    {
	        float: left;
	        height: 100px;
	         100px;
	        background: red;
			margin-right: 10px;/*为了清楚看出两个div是分离的*/
	    }
	    #th2-div
	    {
	        height: 100px;
	        background: orange;
			overflow: hidden;/*触发BFC*/
	    }
	</style>

	<div id="container-div">
	    <div id="th1-div">
	    </div>
	    <div id="th2-div">
	    </div>
	</div>
![](https://i.imgur.com/FBFh3bH.png)

**Example 4**: 根据Example 3的方法触发#th2-div的BFC,同时设置#th2-div的margin-left: 10px;按理说,因为BFC特性,#th1-div与#th2-div应该不会发生重叠,结果应该与Example 3的图一样,但是实验发现,#th2-div的margin区域和#th1-div是重叠的,如下图所示;

	<style type="text/css">
	    #container-div
	    {
	        background: cornflowerblue;
	        height: 200px;
	    }
	    #th1-div
	    {
	        float: left;
	        height: 100px;
	         100px;
	        background: red;
	        /*margin-right: 10px;*/
	    }
	    #th2-div
	    {
	        height: 100px;
	        background: orange;
	        margin-left: 10px;
			overflow: hidden;/*触发BFC*/
	    }
	</style>

	<div id="container-div">
	    <div id="th1-div">
	    </div>
	    <div id="th2-div">
	    </div>
	</div>
![](https://i.imgur.com/8kYYz42.png)
  1. BFC就是一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。

      利用这个特性,我们可以解决父子元素margin重叠的问题。根据BFC特性2我们知道,在同一个BFC里面嵌套的父子Block-Level元素的margin区域是会发生重叠的,如Example 5所示;但如果我们触发父元素生成一个新的BFC,那么它的子元素就不再和父元素处于同一个BFC,从而解决父子元素margin重叠的问题,如Example 6所示。

    Example 5: 在Example 5中,#container-div创建了一个新的BFC,#container-div嵌套着#th1-div,#th1-div嵌套着#th2-div,同时#th1-div设置了margin-top: 10px,#th2-div设置了margin-top: 20px;我们可以看到最终#th1-div和#th2-div基于border-top对齐,两者margin-top重叠;

      <style type="text/css">
         #container-div
         {
             background: cornflowerblue;
             height: 300px;
             overflow: hidden;/*触发BFC*/
         }
         #th1-div
         {
             height: 200px;
              200px;
             background: green;
             margin-top: 10px;
         }
         #th2-div
         {
             height: 100px;
              100px;
             background: orange;
             margin-top: 20px;
         }
     </style>
     <div id="container-div">
         <div id="th1-div">
             <div id="th2-div">
             </div>
     	</div>
     </div>
    

    Example 6: 在Example 5的基础上,我们通过设置overflow: hidden;让#th1-div创建了属于一个BFC,从而让#th1-div和#th2-div不再属于同一个BFC,结果显示#th1-div和#th2-div的margin区域不再重叠,它们成功以我们希望的看到的布局展示。

      <style type="text/css">
         #container-div
         {
             background: cornflowerblue;
             height: 300px;
             overflow: hidden;/*触发BFC*/
         }
         #th1-div
         {
             height: 200px;
              200px;
             background: green;
             margin-top: 10px;
             overflow: hidden;/*触发BFC*/
         }
         #th2-div
         {
             height: 100px;
              100px;
             background: orange;
             margin-top: 20px;
         }
     </style>
     <div id="container-div">
         <div id="th1-div">
             <div id="th2-div">
             </div>
     	</div>
     </div>
    

  2. 计算BFC的高度时,浮动元素也参与计算;

      利用这个特性,我们可以解决浮动元素造成的父元素塌陷问题。 如Example 7所示,默认情况下,浮动元素不会撑开父元素的高度;但是当我们触发了父元素的BFC,如EXample 8所示,这时父元素在计算高度时,会把浮动元素也计算在内,也就解决了父元素塌陷的问题。

    Example 7: 在#th1-div里面放入一个左浮动的#th2-div元素,因为浮动元素脱离了文档流,所以#th2-div元素无法撑开#th1-div的高度,如图所示;

      <style type="text/css">
         #container-div
         {
             background: cornflowerblue;
             height: 300px;
             overflow: hidden;/*触发BFC*/
         }
         #th1-div
         {
              200px;
     		background: green;
         }
         #th2-div
         {
             height: 100px;
     	     100px;
     	    background: orange;
     	    float: left;
         }
     </style>
     <div id="container-div">
         <div id="th1-div">
             <div id="th2-div">
             </div>
     	</div>
     </div>
    

    Example 8: 在Example 8的基础上,通过设置overflow: hidden;触发#th1-div的BCF,使得#th1-div在计算高度的时候会把浮动元素也计算进去,因此#th1-div的高度就变大了。

      <style type="text/css">
         #container-div
         {
             background: cornflowerblue;
             height: 300px;
             overflow: hidden;/*触发BFC*/
         }
         #th1-div
         {
              200px;
     		background: green;
         }
         #th2-div
         {
             height: 100px;
     	     100px;
     	    background: orange;
     	    float: left;
         }
     </style>
     <div id="container-div">
         <div id="th1-div">
             <div id="th2-div">
             </div>
     	</div>
     </div>
    

行内格式化上下文IFC(Inlinel Formatting context)

  在了解IFC之前,必须对Inline-Level元素的Inline-box概念有所了解,需要知道Inline-Level元素在行中的占位是由Inline-box确定的,而非Inline-Level元素的盒子模型确定。同时你需要知道line box的概念,它其实就是包含多个处于一行的Inline-Level元素的行框,多个连续的Inline-Level元素按照从左到右,从上大小的顺序被放置在多个line boxs中。

IFC所规定的渲染规则其实就是对Inline-Level元素在line box中布局的几个关键问题进行了解答,具体如下:

  1. 多个连续的Inline-Level元素是怎样被拆分到多个line boxs中的?

      我的理解是:IFC会先将所有的元素一个个依次首尾相接排列到一起,合并成一个完整的流,然后再确定流中不可拆分的部分,最后将这个流拆分布局到多个不定宽度的line boxs。在上述这个由多个元素组成的流中,不可拆分的部分包括没有空格的连续字符串、单个英文或其他字符、可替换的Inline-Level元素。

      为什么要强调先要合并成完整的流这一过程,主要是想说明在IFC中不是以元素作为最小不可拆分单元的,元素里面的内容可能也可以拆分成多个部分布局到多个line boxs中,比如Example 9,元素之间也可能不可拆分,比如Example 10。

    Example 9:当一个span里面的内容大于line box的宽度,它的内容自动拆分成两个部分,分布在两行(两个line box)中;

     <style type="text/css">
         #container-div
         {
              100px;
             height: 200px;
             background: cornflowerblue;
         }
         span:nth-child(1)/*伪类选择器,选择span标签,同时它要是其父元素的第一个子元素*/
         {
             color: orange;
             background:rebeccapurple;
         }
         span:nth-child(2)
         {
             color: green;
             background: goldenrod;
         }
     </style>
    
     <div id="container-div">
         <span >This is a span</span>
     </div>
    

    Example 10:两个span元素的宽度总和已经大于line box的宽度了,但是第二个元素没有进行换行布局。这是为什么,把它们想成一个整体就好理解了,因为第一个span里面的字符串和第二个span里面的字符串之间不存在空格,因此IFC把他们的内容理解成一个连续的字符串,他们也就成了不可拆分的整体,第二个span也就没有办法进行换行了。

     <style type="text/css">
         #container-div
         {
              100px;
             height: 200px;
             background: cornflowerblue;
         }
         span:nth-child(1)/*伪类选择器,选择span标签,同时它要是其父元素的第一个子元素*/
         {
             color: orange;
             background:rebeccapurple;
         }
         span:nth-child(2)
         {
             color: green;
             background: goldenrod;
         }
     </style>
    
     <div id="container-div">
         <span >ItIsSpan1</span><span>ItIsSpan2</span>
     </div>
    

  2. line box的宽度和高度由什么决定的?

      line box的宽度由包含块和浮动元素决定:当不存在浮动元素时,line box的宽度等于包含块的内容区域的宽度;否则,因为Inline-Level元素会环绕浮动元素布局的特性,line box的宽度会比包含块的内容区域的宽度小。

      line box的高度是由它包含的所有元素的Inline-box决定的,line box的上边界由行中最高的Inline-box上边界确定,line box的下边界由行中最低的Inline-box下边界确定;需要注意不要误以为line box的高度是由行内最高的元素决定;

  3. Inline-Level元素在line boxs中的水平布局由什么决定的?

    如果line box内部内所有Inline-box的总宽度小于line box的宽度,它们在line box中的布局由父元素的text-align属性决定;所以通过设置text-align属性可以实现在父元素中居中的效果,如Example 11所示;

    Example 11:通过设置父元素的text-align:center就能实现行内元素居中效果。

     <style type="text/css">
         #container-div
         {
              100px;
             height: 200px;
             background: cornflowerblue;
     		text-align: center;/*设置内容水平居中*/
         }
         span:nth-child(1)/*伪类选择器,选择span标签,同时它要是其父元素的第一个子元素*/
         {
             color: orange;
             background:rebeccapurple;
         }
         span:nth-child(2)
         {
             color: green;
             background: goldenrod;
         }
     </style>
    
     <div id="container-div">
         <span >A span</span>
     </div>
    

  4. Inline-Level元素在line boxs中的垂直布局由什么决定的?

    Inline-Level元素在line box中的垂直位置由元素的vertical-align属性决定;

  5. 最后一点,line box之间不会存在空隙,也不会发生重叠;

参考资料:

[1] W3C Recommendation

[2] 深入理解BFC

[3] 在网页布局中合理使用inline formating context(IFC)

原文地址:https://www.cnblogs.com/ammyben/p/8280568.html