从清除浮动引起的BFC,IFC,FFC再学习

水平有限,如果存在问题欢迎大家访问我的博客批评指正.

正文

首先,'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,不设置浮动,则他们会默认撑开父盒子

<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. 标签法(不推荐)

<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方式,实现清除浮动.

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

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

未设置overflow

title

设置overflow
title

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

所以也不推荐

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

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

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

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

   .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 规则二

我们看个案例。

<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 规则四

还是举个例子:

<div>元素A</div>
<div>元素B</div>
title

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

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

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

title

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

<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边距问题
<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。再看一下效果是不是不重叠了。

<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
<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、经典的圣杯布局
<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面试上跪了

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345