首先,’FC’肯定不是开封菜,而是Formatting context的简称.而在理解BFC等等概念之前,先让我们了解一下’FC’与’Box’是什么?

-Formatting context 是 W3C CSS2.1 规范中的一个概念。它是页面中的一块渲染区域,并且有一套渲染规则,它决定了其子元素将如何定位,以及和其他元素的关系和相互作用。最常见的 Formatting context 有 Block fomatting context (简称BFC)和 Inline formatting context (简称IFC)以及Flex formatting contexts(简称FFC),最后还有一个GridLayout Formatting Contexts(GFC)。

Box是 CSS 布局的对象和基本单位, 直观点来说,就是一个页面是由很多个 Box 组成的。元素的类型和 display 属性,决定了这个 Box 的类型。 不同类型的 Box, 会参与不同的 Formatting Context(一个决定如何渲染文档的容器),因此Box内的元素会以不同的方式渲染。让我们看看有哪些盒子:

  • block-level box: 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 中才有,暂未仔细研究过。

在我们一些使用场景触发BFC是为了清除浮动(我此次是因为清除浮动才引起的整体学习),所以我还得先讲讲清除浮动.

清除浮动

为什么要清除浮动?

清除浮动主要是为了解决,父元素因为子级元素浮动引起的内部高度为0的问题

1.如下,我给父div设置一个边框,内部放两个div一个left 一个right,不设置浮动,则他们会默认撑开父盒子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<body>
<div class="father">
<div class="left">left</div>
<div class="right">right</div>
</div>
<div class="footer">footer</div>
</body>

<style>
.father{
width: 60vw;
border: 5px solid #000;
}
.left{
width: 30vw;
height: 200px;
background: green;
}
.right{
width: 25vw;
height: 100px;
background: blue;
}
.footer{
width: 60vw;
height: 60px;
background: red;
border:3px solid rgb(194, 246, 6);
}
</style>

title

2.当我给内部两个div加上float属性的时候

title

两个div左右浮动了,而父div因为没高度,只剩下黑色边框了.红色的footer元素也到最上边去了.

那么:

当父元素没有高度的时候,如果子元素不浮动,就回按照子元素高度默认撑开.但是当子元素浮动的时候,父元素就不会自动撑开了.(ps:那么一个浮动,一个不浮动呢?)

父组件没了高度这就是高度坍塌,要解决这个问题,我们首先就会想到clear属性,利用clear:both来解决坍塌问题.

clear 属性定义了元素的哪边上不允许出现浮动元素

  • left 在左侧不允许浮动元素。
  • right 在右侧不允许浮动元素。
  • both 在左右两侧均不允许浮动元素。
  • none 默认值。允许浮动元素出现在两侧。
  • inherit 规定应该从父元素继承 clear 属性的值。

但是具体清除浮动的方式,我们也有好多种思路:

清除浮动的方法

1. 标签法(不推荐)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<body>
<div class="father">
<div class="left">left</div>
<div class="right">right</div>
<div class="clear">我是一个标签</div>
</div>
<div class="footer">footer</div>
</body>

<style>
.father{
width: 60vw;
border: 5px solid #000;
}
.left{
width: 30vw;
height: 200px;
background: green;
}
.right{
width: 25vw;
height: 100px;
background: blue;
}
.footer{
width: 60vw;
height: 60px;
background: red;
border:3px solid rgb(194, 246, 6);
}
.clear{
clear: both;
}
</style>

title

当我们的clear元素内容为空时,就可以借助它不允许两边有浮动元素,那么只能放左右div下边的限制来解决高度坍塌的问题.

  • 优点:简单明了
  • 缺点:添加了无用的标签元素,渲染即使不影响,但是语义化比较差

所以不推荐

2.父级添加overflow属性(不推荐)

通过触发BFC方式,实现清除浮动.

1
2
3
4
5
.fahter{
width: 60vw;
border: 5px solid #000;
overflow: hidden;
}

这种方式明眼一看很简单,但是会在有些操作时如果不自动换行会直接隐藏掉,最简单的例子比如文字.

未设置overflow
title
设置overflow
title

  • 优点:代码简洁
  • 缺点:内容增多的时候容易造成不会自动换行导致内容被隐藏掉,无法显示要溢出的元素

所以也不推荐

3.使用after伪元素(推荐)

1
2
3
4
5
6
7
8
9
.father:after{
content: "";
display: block;
clear:both;
}
.father{
...
zoom: 1
}
  • 优点:符合闭合浮动思想,结构语义化正确

  • 缺点:ie6-7不支持伪元素:after,使用zoom:1触发hasLayout.

4.使用before和after双伪元素(推荐)

1
2
3
4
5
6
7
8
9
10
11
.father:after,.father:before{
content: "";
display: table;
}
.father:after{
clear: both;
}
.father{
...
zoom: 1;
}
  • 优点:代码更简洁

  • 缺点:用zoom:1触发hasLayout.

上面清除浮动的方法里面隐约提到触发BFC来达到目的,但是为什么触发BFC就能清除掉浮动呢.

不急,就让我们从Formatting context来慢慢展开.

FC(Formatting context)

BFC

BFC全称是 block formatting context,也就是 块级格式化上下文,是用于布局块级盒的一块 渲染区域。

1. 触发条件

  • 根元素
  • float的值不是none。
  • position的值不是static或者relative。
  • display的值是inline-block、inline-flex、flex、flow-root、table-caption、table-cell。
  • overflow的值不是visible。

2. 渲染规则

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

这一点其实很好理解.一个接着一个往下排列.

1.3 规则二

我们看个案例。

1
2
3
4
<body>
<div style="width:100px;height:100px;background:red;margin:20px;"></div>
<div style="width:100px;height:100px;background:blue;margin:20px;"></div>
</body>

title

按样式代码来看,红块和蓝块之间间距应该是40px。但实际上是20px。这就是BFC渲染规则的第二点(margin重叠)。

因为BFC的触发条件之一就是根元素,所以body就是一个BFC容器,红块和蓝块是同在body下的相邻块级元素,其margin会生重叠,所以红块和蓝块之间间距只有20px。

1.3 规则三

第三点规则比较难理解我详细解释一下。首先要先理解包含块的含义。

包含块不是一个完整的box,一个完整的box包含margin包含块,border包含块,padding包含块,content包含块。

包含块有可能是box的content包含块,也可能是box的padding包含块。这取决于被包含块所包含的元素的position属性。

例如,如果某个元素position属性是absolute,包含块就是由它的最近的position的值不是 static的父级元素的padding包含块组成。

再例如,如果某个元素的position属性是staticrelative,包含块就是由它的父级元素的content包含块。

如果某个元素的position属性是fixed,包含块就是视口。

那么。BFC中的元素应该与其自身的包含块相接触,而非与BFC相接触,这个包含块有可能是BFC中的一部分,也有可能和bfc无关。

正常元素都是从左往右的格式化,那么对元素的position属性是absolute,right为0,或者元素的float的属性为right,那么就是从右往左的格式化,元素的右边缘和包含块的右边缘接触。

元素H触发生成BFC,其中有两个元素A和元素B,元素A的float的属性为left,那么元素A的左边缘和元素H(也是元素A的包含块)左边缘接触,如果元素B的float的属性也为left,元素B的左边缘不和元素H的左边缘接触,而是和元素A的右边缘接触。这种情况就可以用规则中的“即使包含块中的元素存在浮动也是如此,除非其中元素再生成一个BFC”来解释,元素的float的属性不为none会触发生成BFC,所以这种情况是不矛盾的。

title

1.4 规则四

还是举个例子:

1
2
<div>元素A</div>
<div>元素B</div>

title

当你元素A添加float属性后,元素A脱离文档流。

1
2
<div style='float:left;'>元素A</div>
<div>元素B</div>

正常情况下,后面元知素B会与前道面的元素A重叠(因为float导致A元素脱离文档流不再占据原来的位置,后面元素会占据前面的位置,如图)

title

这个时候给元素B加上overflow:hidden触发BFC;

1
2
<div style='float:left;'>元素A</div>
<div style='overflow:hidden;'>元素B</div>

于是元素B 产生了BFC效果导致元素B收缩,不在占据元素A位置,如图

title

两栏自适应问题的解决方案,有没有!

1.5 规则五

这个规则,可以用来解决内部元素浮动,导致父级元素的高度坍塌问题。其实很好理解,内部元素与外部元素互不影响.

1.6 规则六

也很好理解,高度坍塌问题都解决了,浮动元素的高度肯定要参与BFC的高度计算.

3、应用场景

3.1、清除浮动

在文章最初,提到用clear:both来清除浮动,我们也可以根据BFC的渲染规则第6点(计算BFC容器的高度时,浮动元素也参与计算)来清除浮动,解决高度坍塌的问题。

3.2、解决上下margin边距问题
1
2
3
4
<body>
<div style="width:100px;height:100px;background:red;margin:20px;"></div>
<div style="width:100px;height:100px;background:blue;margin:20px;"></div>
</body>

我们利用BFC渲染规则第2点(属于同一个BFC的两个相邻块级元素的margin会发生重叠),那么不属于同一个BFC的两个相邻块级元素的margin就不会发生重叠。

那么我们在第二个div元素用一个div元素包裹起来,并用overflow:auto触发其BFC。再看一下效果是不是不重叠了。

1
2
3
4
5
6
<body>
<div style="width:100px;height:100px;background:red;margin:20px;"></div>
<div style="overflow:auto">
<div style="width:100px;height:100px;background:blue;margin:20px;"></div>
</div>
</body>

title

3.3、实现自适应两栏布局

自适应两栏布局,是一个主内容区域和一个侧边栏区域组成,两个区域的宽度都可以随窗口大小自适应。我们就可以利用BFC的规则四来实现这个需求.

详见规则四案例.

4. 关于body元素的小问题

有时候我们在body上设置”display:hidden”不能触发BFC.为什么呢?

w3c规定如果overflow不是visible的块级元素将创建BFC。
但是又有一句提醒除非该值已经扩散到了视口

而w3c对于overflow也有定义:

  • UAs需要将根元素上的overflow属性置于视窗之上。
  • overflow扩散行为:当根元素是html元素且overflow为visible,而且html元素有body作为其子元素,UAs则需要将第一个body之上的overflow属性应用于视窗。
  • 用于视窗的overflow: visible将被解析为overflow: auto。
  • overflow扩散行为将导致body的overflow属性为visible。

分析:

  1. html元素的overflow属性默认是visible,并且有body作为子元素,那么body的overflow属性将被用于视窗,产生了扩散行为.
  2. body元素的overflow被设置成了hidden,但是扩散行为导致body元素属性为visible.

所以
所以只能先将html元素的overflow属性设置为hidden,再把body元素的overflow属性设置为hidden,才能使body触发BFC 。

IFC

IFC全称是inline formatting context,也就是 内联格式化上下文,是用于布局内联元素盒子的一块 渲染区域。

1、触发条件

IFC的形成条件非常简单,块级元素中仅包含内联级别元素,需要注意的是当IFC中有块级元素插入时,会产生两个匿名块将父元素分割开来,产生两个IFC。

2、渲染规则

  • 子元素水平方向横向排列,并且垂直方向起点为元素顶部。
  • 子元素只会计算横向样式空间,【padding、border、margin】,垂直方向样式空间不会被计算,【padding、border、margin】。
  • 在垂直方向上,子元素会以不同形式来对齐(vertical-align)
  • 能把在一行上的框都完全包含进去的一个矩形区域,被称为该行的行框(line box)。行框的宽度是由包含块(containing box)和与其中的浮动来决定。
  • IFC中的“line box”一般左右边贴紧其包含块,但float元素会优先排列。
  • IFC中的“line box”高度由 CSS 行高计算规则来确定,同个IFC下的多个line box高度可能会不同。
  • 当 inline-level boxes的总宽度少于包含它们的line box时,其水平渲染规则由 text-align 属性值来决定。
  • 当一个“inline box”超过父元素的宽度时,它会被分割成多个boxes,这些 boxes 分布在多个“line box”中。如果子元素未设置强制换行的情况下,“inline box”将不可被分割,将会溢出父元素。

3、应用场景

  • 水平居中:当一个块要在环境中水平居中时,设置其为inline-block则会在外层产生IFC,通过text-align则可以使其水平居中。
  • 垂直居中:创建一个IFC,用其中一个元素撑开父元素的高度,然后设置其vertical-align:middle,其他行内元素则可以在此父元素下垂直居中。

FFC

FFC全称是flex formatting context, 也就是 灵活格式化上下文,是用于布局弹性容器的一块 渲染区域。

1、触发条件

父级元素设置display:flex或者`display:inline-flex

2、渲染规则`

可以看阮一峰的Flex 布局教程:语法篇,讲的非常详细。

要注意一点。生成FFC后,其子元素的float、clear和vertical-align属性将失效。

3、应用场景

1、自动撑开页面高度,底栏总是出现在页面的底部

title

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<style>
.wrap{
display:flex;
padding:0;
margin:0;
min-height:100vh;
flex-direction:column;
}
.main{
flex-grow:1;
}
</style>
<body class="wrap">
<header style="line-height:50px;background:red;color:#fff;text-align:center">头部</header>
<main class="main">内容</main>
<footer style="line-height:50px;background:#eeeeee;color:#333;text-align:center">底栏</footer>
</body>

title

2、经典的圣杯布局
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<style>
.wrap {
display: flex;
padding: 0;
margin: 0;
min-height: 100vh;
flex-direction: column;
}
header,
footer {
flex: 0 0 50px;
}

.content {
display: flex;
flex: 1
}

.main {
flex: 1;
}
.nav,
.ads{
flex: 0 0 100px;
background:green;
}
.nav{
order:-1;
background:yellow;
}
</style>
<body class="wrap">
<header style="line-height:50px;background:red;color:#fff;text-align:center">头部</header>
<div class="content">
<main class="main">内容区</main>
<nav class="nav">侧边导航</nav>
<aside class="ads">侧边栏</aside>
</div>
<footer style="line-height:50px;background:#eeeeee;color:#333;text-align:center">底栏</footer>
</body>

title

GFC

GFC全称是gridLayout formatting context,也就是 网格布局格式上下文,是用于布局网格容器的一块 渲染区域。

1、触发条件

当为一个元素设置display值为grid的时候,此元素将会获得一个独立的渲染区域,我们可以通过在网格容器(grid container)上定义网格定义行(grid definition rows)和网格定义列(grid definition columns)属性各在网格项目(grid item)上定义网格行(grid row)和网格列(grid columns)为每一个网格项目(grid item)定义位置和空间。

2. 应用场景

那么GFC有什么用呢,和table又有什么区别呢?首先同样是一个的表格,但GridLayout会有更加丰富的属性来控制行列,控制对齐以及更为精细的渲染语义和控制。

最后:关于zoom知识的扩展
zoom:1是针对ie的属性,可以激活hasLayout属性,关于hasLayout

haslayout 是Windows Internet Explorer渲染引擎的一个内部组成部分。在InternetExplorer中,一个元素要么自己对自身的内容进行计算大小和组织,要么依赖于父元素来计算尺寸和组织内容。为了调节这两个不同的概念,渲染引擎采用了 hasLayout 的属性,属性值可以为true或false。当一个元素的 hasLayout属性值为true时,我们说这个元素有一个布局(layout)

要想更好的理解 css, 尤其是 IE 下对 css 的渲染,haslayout 是一个非常有必要彻底弄清楚的概念。大多IE下的显示错误,就是源于 haslayout。如果它设置成了true,它就不得不去渲染它自己,因此元素不得不扩展去包含它的流出的内容。例如浮动或者很长很长的没有截断的单词,如果haslayout没有被设置成true,那么元素得依靠某个祖先元素来渲染它。这就是很多的ie bugs诞生的地方。

参考文章:
红尘炼心-四年前端在CSS面试上跪了