您的位置:新葡亰496net > 新葡亰官网 > 新葡亰496net:浅谈格式化上下文

新葡亰496net:浅谈格式化上下文

发布时间:2019-11-10 05:58编辑:新葡亰官网浏览(111)

    学习BFC

    2015/08/21 · CSS · BFC

    原文出处: elcarim的博客   

    BFC全称是Block Formatting Context,即块格式化上下文。它是CSS2.1规范定义的,关于CSS渲染定位的一个概念。要明白BFC到底是什么,首先来看看什么是视觉格式化模型。

    本文是我在学习CSS的bfc过程中进行的总结归纳。如果你发现文章存在错误,请务必向我联系,以免误导更多的读者,谢谢。

    视觉格式化模型

    视觉格式化模型(visual formatting model)是用来处理文档并将它显示在视觉媒体上的机制,它也是CSS中的一个概念。

    视觉格式化模型定义了盒(Box)的生成,盒主要包括了块盒、行内盒、匿名盒(没有名字不能被选择器选中的盒)以及一些实验性的盒(未来可能添加到规范中)。盒的类型由display属性决定。


    块盒(block box)

    块盒有以下特性:

    • 当元素的CSS属性displayblocklist-item或 table时,它是块级元素 block-level;
    • 视觉上呈现为块,竖直排列;
    • 块级盒参与(块格式化上下文);
    • 每个块级元素至少生成一个块级盒,称为主要块级盒(principal block-level box)。一些元素,比如<li>,生成额外的盒来放置项目符号,不过多数元素只生成一个主要块级盒。

    格式化上下文( formatting contexts )
    ├── 块级格式化上下文( Block formatting contexts )( BFC )
    ├── 行内格式化上下文( Inline formatting contexts ) ( IFC )
    ├── 自适应格式化上下文( Flex Formatting Contexts )( FFC )
    └── 网格布局格式化上下文( GridLayout Formatting Contexts )( GFC )

    行内盒(inline box)

    • 当元素的CSS属性display的计算值为inlineinline-blockinline-table时,称它为行内级元素;
    • 视觉上它将内容与其它行内级元素排列为多行;典型的如段落内容,有文本(可以有多种格式譬如着重),或图片,都是行内级元素;
    • 行内级元素生成行内级盒(inline-level boxes),参与行内格式化上下文(inline formatting context)。同时参与生成行内格式化上下文的行内级盒称为行内盒(inline boxes)。所有display:inline的非替换元素生成的盒是行内盒;
    • 不参与生成行内格式化上下文的行内级盒称为原子行内级盒(atomic inline-level boxes)。这些盒由可替换行内元素,或 display 值为 inline-block 或inline-table 的元素生成,不能拆分成多个盒;

    格式化上下文( formatting contexts )

    其实要搞懂格式化上下文( FC ),重点不在于记着哪几个条件能创建新的FC,而在于FC是用来干嘛的。
    知乎上winter有一个回答说得很不错,可惜他删掉答案了,原话如下:

    如何理解块级格式化上下文BFC?
    首先我觉得应该改改翻译,formatting在印刷业主要就是排版的意思,它只有在计算机行业,宾语是磁盘的时候才应该翻译成格式化。在css里面,应该取排版的原意。另外我觉得谈BFC,一定要和IFC一起讲,类似的东西还有FFC、table以及Grid。其实BFC不是知识点,它只是一个概念,这块知识应该是visual formatting,其实就是css的排版系统,如果你纠结于BFC这个概念,不去理解normal flow、block/inline level、block container、replaced element等等这些相关的概念,以及css排版的策略,你就永远没法真正理解BFC,你就永远只能死记硬背overflow不为visible就会触发BFC这样的话。

    这里他提出formatting有排版的意思,无论这个翻译你是否接受,不能否认的是他向我们提出了新的观点。既然formatting在印刷中的代表排版,类比思考一下,FC在css中可否理解成布局环境的意思。或者说,如果我需要一个新的布局环境,该环境内、外元素互不干扰,这时候我们就通过引入bfc等来完成这操作。当然,我们一般还是将FC写成格式化上下文比较规范,便于交流。


    在普通流中的 Box(框) 属于一种 formatting context(格式化上下文) ,类型可以是 block ,或者是 inline ,但不能同时属于这两者。并且, Block boxes(块框) 在 block formatting context(块格式化上下文) 里格式化, Inline boxes(块内框) 则在 inline formatting context(行内格式化上下文) 里格式化。任何被渲染的元素都属于一个 box ,并且不是 block ,就是 inline 。即使是未被任何元素包裹的文本,根据不同的情况,也会属于匿名的 block boxes 或者 inline boxes。所以上面的描述,即是把所有的元素划分到对应的 formatting context 里。

    上文提到:所有的框都属于一种格式化上下文,所有的元素都划分到一种格式化上下文中被初始化。
    但你是否思考过以下问题:有没有元素不属于任何一种格式化上下文?
    <div overflow='hidden'><div id='inner'><div></div>,毫无疑问的是,内层的div被外层div创建的bfc中被初始化,那么,<div ><div id='inner'><div></div>,这两个在普通流中的框,被谁创建的bfc初始化?这看上去框就是纯粹的在普通流中,没被任何bfc初始化。
    这时候你就想想哪些元素会生成bfc?其中一个就是根元素。什么是根元素( 那里面的几个答案都值得看 ),一般指的是顶层的html元素,那就是说上文那两个div都是在根元素创建的bfc下被渲染的,他们都存在于html的bfc中。注意,他们都存在于同一个bfc中,所以,所有的元素都属于(在)一种格式化上下文中。这事还没完,回到经典的父框没被浮动的子框撑起来例子。

    新葡亰496net 1

    `<div border='5px dashed black'><div float=left></div></div>`

    看过BFC产生条件及布局规则,你是否会疑惑:既然外层div在bfc中,浮动元素又创建了bfc,那么根据BFC布局规则最后一条,为何浮动元素没计算进外层div高度?
    这个问题的目的是让你区分,“在bfc”跟“创建bfc”的区别。外层div是在html创建的bfc中,但它自己没创建bfc,浮动元素才创建了bfc。所以,外层div根本不是bfc,只不过它是在bfc的环境中被初始化。所以外层div不是bfc,计算高度不是按照bfc的方法来计算。给html加上border就能更好理解。因为html作为根元素,它产生了bfc环境,我们说它就是一个bfc,它是bfc,就按bfc的方法计算高度,里面的浮动元素也参与计算,所以就被撑起来了。

    新葡亰496net 2

    html{border:5px solid red;}

    搞清楚bfc的原理后,也知道了为什么父元素撑不起浮动子元素,归根结底,如果父元素要撑起里面的后代元素,必须是该父元素自己声明了bfc,让后代元素在父元素创建的容器中。


    第一次写这种技术文,可能行文有点乱,希望能帮助到有需要的人。最后,非常感谢那些付出自己知识的大神们,希望你在浏览的同时感谢他们的贡献的贡献。
    [1] BFC 神奇背后的原理
    [2] BFC是什么
    [3] CSS定位机制之一:普通流
    [4] 根元素
    [5] 常规流
    [6] normal-flow

    匿名盒(anonymous box)

    匿名盒也有份匿名块盒与匿名行内盒,因为匿名盒没有名字,不能利用选择器来选择它们,所以它们的所有属性都为inherit或初始默认值;

    如下面例子,会创键匿名块盒来包含毗邻的行内级盒:

    XHTML

    <div> Some inline text <p>followed by a paragraph</p> followed by more inline text. </div>

    1
    2
    3
    4
    5
    <div>
        Some inline text
        <p>followed by a paragraph</p>
        followed by more inline text.
    </div>

    新葡亰496net 3

    三个定位方案

    在定位的时候,浏览器就会根据元素的盒类型和上下文对这些元素进行定位,可以说盒就是定位的基本单位。定位时,有三种定位方案,分别是常规流,浮动已经绝对定位。

    常规流(Normal flow)

    • 在常规流中,盒一个接着一个排列;
    • 块级格式化上下文里面, 它们竖着排列;
    • 行内格式化上下文里面, 它们横着排列;
    • positionstaticrelative,并且floatnone时会触发常规流;
    • 对于静态定位(static positioning),position: static盒的位置是常规流布局里的位置
    • 对于相对定位(relative positioning),position: relative,盒偏移位置由这些属性定义topbottomleftandright即使有偏移,仍然保留原有的位置,其它常规流不能占用这个位置。

    浮动(Floats)

    • 盒称为浮动盒(floating boxes);
    • 它位于当前行的开头或末尾;
    • 导致常规流环绕在它的周边,除非设置 clear 属性;

    绝对定位(Absolute positioning)

    • 绝对定位方案,盒从常规流中被移除,不影响常规流的布局;
    • 它的定位相对于它的包含块,相关CSS属性:topbottomleftright
    • 如果元素的属性positionabsolutefixed,它是绝对定位元素;
    • 对于position: absolute,元素定位将相对于最近的一个relativefixedabsolute的父元素,如果没有则相对于body

    块格式化上下文

    到这里,已经对CSS的定位有一定的了解了,从上面的信息中也可以得知,块格式上下文是页面CSS 视觉渲染的一部分,用于决定块盒子的布局及浮动相互影响范围的一个区域

    BFC的创建方法

    • 根元素或其它包含它的元素;
    • 浮动 (元素的float不为none);
    • 绝对定位元素 (元素的positionabsolutefixed新葡亰496net,);
    • 行内块inline-blocks(元素的 display: inline-block);
    • 表格单元格(元素的display: table-cell,HTML表格单元格默认属性);
    • overflow的值不为visible的元素;
    • 弹性盒 flex boxes (元素的display: flexinline-flex);

    但其中,最常见的就是overflow:hiddenfloat:left/rightposition:absolute。也就是说,每次看到这些属性的时候,就代表了该元素以及创建了一个BFC了。

    BFC的范围

    BFC的范围在MDN中是这样描述的。

    A block formatting context contains everything inside of the element creating it that is not also inside a descendant element that creates a new block formatting context.

    中文的意思一个BFC包含创建该上下文元素的所有子元素,但不包括创建了新BFC的子元素的内部元素。

    这段看上去有点奇怪,我是这么理解的,加入有下面代码,class名为.BFC代表创建了新的块格式化:

    XHTML

    <div id='div_1' class='BFC'> <div id='div_2'> <div id='div_3'></div> <div id='div_4'></div> </div> <div id='div_5' class='BFC'> <div id='div_6'></div> <div id='div_7'></div> </div> </div>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <div id='div_1' class='BFC'>
    <div id='div_2'>
    <div id='div_3'></div>
    <div id='div_4'></div>
    </div>
    <div id='div_5' class='BFC'>
    <div id='div_6'></div>
    <div id='div_7'></div>
    </div>
    </div>

    这段代码表示,#div_1创建了一个块格式上下文,这个上下文包括了#div_2#div_3#div_4#div_5。即#div_2中的子元素也属于#div_1所创建的BFC。但由于#div_5创建了新的BFC,所以#div_6#div_7就被排除在外层的BFC之外。

    我认为,这从另一方角度说明,一个元素不能同时存在于两个BFC中

    BFC的一个最重要的效果是,让处于BFC内部的元素与外部的元素相互隔离,使内外元素的定位不会相互影响。这是利用BFC清除浮动所利用的特性,关于清除浮动将在后面讲述。

    如果一个元素能够同时处于两个BFC中,那么就意味着这个元素能与两个BFC中的元素发生作用,就违反了BFC的隔离作用,所以这个假设就不成立了。

    BFC的效果

    就如刚才提到的,BFC的最显著的效果就是建立一个隔离的空间,断绝空间内外元素间相互的作用。然而,BFC还有更多的特性:

    Floats, absolutely positioned elements, block containers (such as inline-blocks, table-cells, and table-captions) that are not block boxes, and block boxes with ‘overflow’ other than ‘visible’ (except when that value has been propagated to the viewport) establish new block formatting contexts for their contents.

    In a block formatting context, boxes are laid out one after the other, vertically, beginning at the top of a containing block. The vertical distance between two sibling boxes is determined by the ‘margin’ properties. Vertical margins between adjacent block-level boxes in a block formatting context collapse.

    In a block formatting context, each box’s left outer edge touches the left edge of the containing block (for right-to-left formatting, right edges touch). This is true even in the presence of floats (although a box’s line boxes may shrink due to the floats), unless the box establishes a new block formatting context (in which case the box itself may become narrower due to the floats).

    简单归纳一下:

    1. 内部的盒会在垂直方向一个接一个排列(可以看作BFC中有一个的常规流);
    2. 处于同一个BFC中的元素相互影响,可能会发生margin collapse;
    3. 每个元素的margin box的左边,与容器块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此;
    4. BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素,反之亦然;
    5. 计算BFC的高度时,考虑BFC所包含的所有元素,连浮动元素也参与计算;
    6. 浮动盒区域不叠加到BFC上;

    这么多性质有点难以理解,但可以作如下推理来帮助理解:html的根元素就是<html>,而根元素会创建一个BFC,创建一个新的BFC时就相当于在这个元素内部创建一个新的<html>,子元素的定位就如同在一个新<html>页面中那样,而这个新旧html页面之间时不会相互影响的。

    上述这个理解并不是最准确的理解,甚至是将因果倒置了(因为html是根元素,因此才会有BFC的特性,而不是BFC有html的特性),但这样的推理可以帮助理解BFC这个概念。

    从实际代码来分析BFC

    讲了这么多,还是比较难理解,所以下面通过一些例子来加深对BFC的认识。

    实例一

    CSS

    <style> * { margin: 0; padding: 0; } .left{ background: #73DE80; /* 绿色 */ opacity: 0.5; border: 3px solid #新葡亰496net:浅谈格式化上下文。F31264; width: 200px; height: 200px; float: left; } .right{ /* 粉色 */ background: #EF5BE2; opacity: 0.5; border: 3px solid #F31264; width:400px; min-height: 100px; } .box{ background:#888; height: 100%; margin-left: 50px; } </style> <div class='box'> <div class='left'> </div> <div class='right'> </div> </div>

    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
    <style>
    * {
    margin: 0;
    padding: 0;
    }
    .left{
    background: #73DE80; /* 绿色 */
    opacity: 0.5;
    border: 3px solid #F31264;
    width: 200px;
    height: 200px;
    float: left;
    }
    .right{ /* 粉色 */
    background: #EF5BE2;
    opacity: 0.5;
    border: 3px solid #F31264;
    width:400px;
    min-height: 100px;
    }
    .box{
    background:#888;
    height: 100%;
    margin-left: 50px;
    }
    </style>
    <div class='box'>
    <div class='left'> </div>
    <div class='right'> </div>
    </div>

    显示效果:

    新葡亰496net 4

    绿色框(’#left’)向左浮动,它创建了一个新BFC,但暂时不讨论它所创建的BFC。由于绿色框浮动了,它脱离了原本normal flow的位置,因此,粉色框(’#right’)就被定位到灰色父元素的左上角(特性3:元素左边与容器左边相接触),与浮动绿色框发生了重叠。

    同时,由于灰色框(’#box’)并没有创建BFC,因此在计算高度的时候,并没有考虑绿色框的区域(特性6:浮动区域不叠加到BFC区域上),发生了高度坍塌,这也是常见问题之一。

    实例二

    现在通过设置overflow:hidden来创建BFC,再看看效果如何。

    XHTML

    .BFC{ overflow: hidden; } <div class='box BFC'> <div class='left'> </div> <div class='right'> </div> </div>

    1
    2
    3
    4
    5
    6
    7
    8
    .BFC{
    overflow: hidden;
    }
     
    <div class='box BFC'>
    <div class='left'> </div>
    <div class='right'> </div>
    </div>

    新葡亰496net 5

    灰色框创建了一个新的BFC后,高度发生了变化,计算高度时它将绿色框区域也考虑进去了(特性5:计算BFC的高度时,浮动元素也参与计算);

    而绿色框和红色框的显示效果仍然没有任何变化。

    实例三

    现在,现将一些小块添加到粉色框中,看看效果:

    XHTML

    <style> .little{ background: #fff; width: 50px; height: 50px; margin: 10px; float: left; } </style> <div class='box BFC'> <div class='left'> </div> <div class='right'> <div class='little'></div> <div class='little'></div> <div class='little'></div> </div> </div>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    <style>
    .little{
    background: #fff;
    width: 50px;
    height: 50px;
    margin: 10px;
    float: left;
    }
    </style>
     
    <div class='box BFC'>
    <div class='left'> </div>
    <div class='right'>
    <div class='little'></div>
    <div class='little'></div>
    <div class='little'></div>
    </div>
    </div>

    新葡亰496net 6

    由于粉色框没有创建新的BFC,因此粉色框中白色块受到了绿色框的影响,被挤到了右边去了。先不管这个,看看白色块的margin。

    实例四

    利用同实例二中一样的方法,为粉色框创建BFC:

    XHTML

    <div class='box BFC'> <div class='left'> </div> <div class='right BFC'> <div class='little'></div> <div class='little'></div> <div class='little'></div> </div> </div>

    1
    2
    3
    4
    5
    6
    7
    8
    <div class='box BFC'>
    <div class='left'> </div>
    <div class='right BFC'>
    <div class='little'></div>
    <div class='little'></div>
    <div class='little'></div>
    </div>
    </div>

    新葡亰496net 7

    一旦粉色框创建了新的BFC以后,粉色框就不与绿色浮动框发生重叠了,同时内部的白色块处于隔离的空间(特性4:BFC就是页面上的一个隔离的独立容器),白色块也不会受到绿色浮动框的挤压。

    总结

    以上就是BFC的分析,BFC的概念比较抽象,但通过实例分析应该能够更好地理解BFC。在实际中,利用BFC可以闭合浮动(实例二),防止与浮动元素重叠(实例四)。同时,由于BFC的隔离作用,可以利用BFC包含一个元素,防止这个元素与BFC外的元素发生margin collapse。

    参考

    视觉格式化模型 | MDN

    块格式化上下文| MDN

    CSS之BFC详解

    W3C block-formatting

    1 赞 5 收藏 评论

    新葡亰496net 8

    本文由新葡亰496net发布于新葡亰官网,转载请注明出处:新葡亰496net:浅谈格式化上下文

    关键词: