zoukankan      html  css  js  c++  java
  • 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)

  • 相关阅读:
    UITableview cell 的多选
    抽屉开关控制器
    NSDate 获取明天、后天的日期
    UITextField里面的 placeholder颜色和字体
    判断返回数据是否为 null
    UIButton 长按点击 背景改变效果
    UIButton 去除按下效果(阴影)
    iOS-RunLoop,为手机省电,节省CPU资源,程序离不开的机制
    iOS-真机调试
    iOS-设置启动图片
  • 原文地址:https://www.cnblogs.com/ammyben/p/8280568.html
Copyright © 2011-2022 走看看