美文网首页
从清除浮动引起的BFC,IFC,FFC再学习

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

作者: DLLCNX | 来源:发表于2020-05-13 10:33 被阅读0次

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

    正文

    首先,'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面试上跪了

    相关文章

      网友评论

          本文标题:从清除浮动引起的BFC,IFC,FFC再学习

          本文链接:https://www.haomeiwen.com/subject/bbngnhtx.html