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;
    width: 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;
width: 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;
width: 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;
    width: 200px;
    background: green;
    margin-top: 10px;
    }
    #th2-div
    {
    height: 100px;
    width: 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;
    width: 200px;
    background: green;
    margin-top: 10px;
    overflow: hidden;/*触发BFC*/
    }
    #th2-div
    {
    height: 100px;
    width: 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
    {
    width: 200px;
    background: green;
    }
    #th2-div
    {
    height: 100px;
    width: 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
    {
    width: 200px;
    background: green;
    }
    #th2-div
    {
    height: 100px;
    width: 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
    {
    width: 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
    {
    width: 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
    {
    width: 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)

CSS的两种格式化上下文:BFC和IFC的更多相关文章

  1. css的两种盒子模型

    css的两种盒子模型:W3C标准盒子模型.IE盒子模型 两者的相同之处:都包含margin.border.padding.content 两者的不同之处:W3C标准盒子模型的content部分不包含其 ...

  2. 引入外部CSS的两种方式及区别

    1.CSS的两种引入方式 通过@import指令引入 @import指令是CSS语言的一部分,使用时把这个指令添加到HTML的一个<style>标签中: 要与外部的CSS文件关联起来,得使 ...

  3. css中margin重叠和一些相关概念(包含块containing block、块级格式化上下文BFC、不可替换元素 non-replaced element、匿名盒Anonymous boxes )

    平时在工作中,总是有一些元素之间的边距与设定的边距好像不一致的情况,一直没明白为什么,最近仔细研究了一下,发现里面有学问:垂直元素之间的margin有有互相重叠的情况:新建一个BFC后,会阻止元素与外 ...

  4. WordPress引入css/js两种方法

    WordPress引入css/js 是我们制作主题时首先面对的一个难点,任何一款主题都要加载自己的css,js,甚至很有可能还需要加载Jquery文件,网上方法特多,说法不一,我们今天借鉴wordpr ...

  5. CSS的两种盒模型

    盒模型一共有两种模式,一种是标准模式,另一种就是怪异模式. 当你用编辑器新建一个html页面的时候你一定会发现最顶上都会有一个DOCTYPE标签,例如: <!DOCTYPE HTML PUBLI ...

  6. css中两种居中方式text-align:center和margin:0 auto 的使用场景

    关于使用text-align:center和margin:0 auto 两种居中方式的比较 前言:最近由于要学习后端,需要提前学习一部分前端知识,补了补css知识,发现狂神在讲这一部分讲的不是特别清楚 ...

  7. 块级格式化上下文(BFC)

    一.什么是BFC 具有BFC属性的元素也属于普通流定位方式,与普通容器没有什么区别,但是在功能上,具有BFC的元素可以看做是隔离了的独立容器,容器里面的元素不会在布局上影响到外面的元素,并且具有普通容 ...

  8. css垂直居中 两种方法

    在前端面试的时候我们经常会被问道怎样使一个元素在页面垂直居中呢,这也是一个老生常谈的问题了. 解决的方法基本都是使用定位来实现 div{display: fixed;left: 50%;top: 50 ...

  9. 引入css的两种方式

    摘自:https://www.cnblogs.com/gyjWEB/p/4831646.html 在HTML中引入css的其中的两个方法: 1.如果使用链接式,需要使用如下的语句引入外部css文件: ...

随机推荐

  1. sql 语句设置主键

    创建表时候 SQL code? 1 2 3 4 CREATE TABLE tb ( id INT IDENTITY(1,1) PRIMARY KEY, ) 添加时候 SQL code? 1 2 ALT ...

  2. 安装jdk1.8,编写环境变量

    好记性不如烂笔头!!!(我这是把jdk放在的/usr/local下,且命令为jdk1.8...复制的时候别搞错位置了) JAVA_HOME=/usr/local/jdk1./ JAVA_BIN=/us ...

  3. 1001 害死人不偿命的(3n+1)猜想 (15 分)

    #include <iostream> using namespace std; int main(){ ; cin >> n; ){ != ) n = ( * n + ) / ...

  4. Angular 2: 404 error occur when I refresh through the browser [duplicate]

    https://stackoverflow.com/questions/35284988/angular-2-404-error-occur-when-i-refresh-through-the-br ...

  5. adb自救指南

    以下只能自救专用 adb.exe root adb remount adb.exe connect <Ip> adb.exe install [-r] [-d] <apkPath&g ...

  6. 面向对象中的@classonlymethod 与 @classmethod的区别

    如果要使用classonlymethod ,则需要先定义好一个classonlymethod 类. 首先我们需要明白无论是classonlymethod还是classmethod,本质都是一个类,而c ...

  7. requests模块的一些总结

    一.cookie模拟登录的三种方式 #!/usr/bin/env python # -*- coding: utf-8 -*- #author tom import requests post_url ...

  8. 安装配置flutter环境

    flutter 的中文文档 https://flutterchina.club/get-started/install/ github 地址 https://github.com/flutter/fl ...

  9. EntityFramework 建立一对一关系

    前言:本来要使用实体拆分实现一对一,但发现查询时无法单独查询,影响效率,故改用手动建立一对一关系 例: 实体类: public class TestDbContext : DbContext { pu ...

  10. 如何在64位WIN7下安装64位的解压版mysql-5.6.37-winx64.zip

    1.到mysql官网下载 https://cdn.mysql.com//Downloads/MySQL-5.6/mysql-5.6.37-winx64.zip 2.将解压缩后的文件放到自己想要的地方, ...