一个div 上下两行_CSS:BFC块格式化上下文

43830d2c1cd5250c509b5e26e991f03e.png

2020.6.9修订

因为前几天面试问到了BFC答不上来,最后是挂了。然后各博客包括MDN看了感觉定义里都不对劲,花了半天在找BFC的规范,就在这里打个mark。

很多文章都认为弹性容器flex container才会建立BFC,MDN一枝独秀认为其item才建立新BFC:

弹性元素( displayflexinline-flex元素 的直接子元素

看了英文页面历史,有人把flex container给加上了,后来又被另一个人删了(没留注释为什么删),后来又是不同的另一个人又加了这个flex item(还是没有注释)

6.24更新,和csswg那边提东西了,别人去改MDN,感觉越改越离谱。。。至于怎么回事就继续看吧

后文所述“开启BFC”均指“新建了独立的BFC”

引言

BFC即块格式化上下文(Block Formatting Context),注意第一个单词是[block:块]不是[block-level:块级]

初学者听这个名字肯定感觉很陌生,CSS会不会还有很多名词?
笔者能想到除了BFC还有可替换元素(replaced element),暂时想不出其它了。。因为笔者也是新手

为什么要新建块格式化上下文

很多种启动方式的总结在这里,不搬了:MDN Block formatting context

主流启动方式:常用的有display:inline-block,overflow:auto/hidden,未来的方法有display:flow-root

在下文我们会提到,格式化上下文是用于影响内部布局的,文档根元素<html>会开启新的块格式化上下文(憋问,我是看MDN它说是就是),如果内部的元素不继续开上下文,就一直是继承html的这个块格式化上下文,但是就算新建了,布局还是不会变,依旧是块布局。

虽然我们没有改变布局,但是我们开启了独立的布局,这个新建了块级格式化上下文的盒子/根(block formatting context root)本身它多了一些特性,有三个特点是新建BFC后这个盒子会变得与众不同的:

  • 建立了新的BFC的盒不和后代元素发生margin collapse(外边距折叠,外边距合并)
  • 建立新的BFC的盒不会和其所处的同一个块格式化上下文中的浮动元素重叠。
  • 建立新的BFC的盒height:auto时,会被内部的浮动元素撑开(clearfix)

不会和后代margin合并

有关垂直margin合并的基础可以看W3school这里
因为垂直margin合并只发生在同一个块格式化上下文内,建立了新BFC的盒隶属于更上一层BFC,其后代元素隶属于其父元素的BFC。

a6a701f3ec58e020f41f158cfcc4e05b.png

父子元素的margin合并其实日常都见不到,因为写CSS嘛,一般都要加padding或者border加到这个父元素上,挡在了两个margin之间...这里点到为止,没有实战意义...

排除外部浮动

e2f494c5745b41dd1d647aa3f4da059e.png
没有使用BFC时,外部浮动和本div重叠-----根据浮动元素的特性,浮动元素允许内联和文本元素环绕它,所以如果蓝块里有内联和文本元素,依旧能保持不被遮挡(像word四周环绕那样,说起来好久没见过这种布局的文章咯),是块级元素就会被盖住背景

daa88d3909a90b6bc075ab9b70da918b.png
BFC实现两栏:一栏宽度固定另一栏自适应


上面这个布局的好处在于,蓝色块并不需要指定margin-left的值就实现了宽度自适应(此例引用自张鑫旭)

包含内部浮动

662a1859792fb9228faa730448acd780.png
是这样吗

经常遇到浮动导致父级块坍塌的问题吧?粉色块开启新的BFC,撑开后代浮动,藏青块没有开新的BFC。

大家牢记这三个特性

以上就是引言了,感谢大家的阅读!散了散了

正文

正文存在大量非干货知识请注意,面试官是不会问这些哒

格式化上下文

CSS display 3
A formatting context is the environment into which a set of related boxes are laid out. Different formatting contexts lay out their boxes according to different rules.
...
Additionally, some types of formatting contexts interleave and co-exist...
A box either establishes a new ... or continues the formatting context of its containing block....
The type of formatting context established by the box is determined by its inner display type....
  • 不同的格式化上下文布局不同
    不同的格式化上下文根据不同的规则排列它们的框,比如块格式化上下文就是垂直排列元素的,内联格式化上下文就是行内水平排,弹性flex格式化上下文就是弹性排列
  • 格式化上下文可能共存②
    内联格式化上下文是能存在于块格式化上下文中的,因为这两个都是流式布局。co-exist的解释非常牵强,有写手表示不满issues/5105
  • 一个框要么建立一个新的独立格式上下文,要么承接其包含块的格式上下文。
  • 如果有一个块生成了新的格式化上下文,这个新的格式化上下文的类型由其inner display属性决定,可以理解为display属性决定。

相关规范CSS2考古

1、margin合并

CSS2只定义了格式化上下文和内联格式化上下文两种上下文,一个垂直排元素一个水平排元素,大家自然是熟悉的:

CSS2 --- 9.4.1 Block formatting contexts
...Vertical margins between adjacent block-level boxes in a block formatting context collapse...

在BFC的定义里提到,垂直margin合并现象会在内部发生

在CSS display 3里有解释另一个名词

independent formatting context(独立的格式化上下文) .... .... margins do not collapse across formatting context boundaries.

边距不会在(注:任何两个)格式化上下文的边界上合并。(这是开启BFC抑制边距折叠的原理,仅仅是因为启动了新的格式化上下文)

3、排除外部浮动

在BFC的定义里,只提到了前文讲到的一个特点,margin合并,因为另外两个有关浮动的特性分别定义在浮动一章和另一章:

CSS2 --- 9.5 Floats
The border box of a table, a block-level replaced element, or an element in the normal flow that establishes a new block formatting context (such as an element with 'overflow' other than 'visible') must not overlap the margin box of any floats in the same block formatting context as the element itself. If necessary, implementations should clear the said element by placing it below any preceding floats, but may place it adjacent to such floats if there is sufficient space.

table的border box、块级替换元素(replaced element)或建立新块格式上下文的正常流中的元素不能与元素本身位于同一块格式上下文中的任何浮动的边距框重叠。如有必要,实现应通过将所述元素放置在任何前面的浮动的下面,但如果有足够的空间,则可以将其放置在这些浮动旁边。双栏布局的原理

3、清除内部浮动

CSS2 --- 10.6.7 'Auto' heights for block formatting context roots
if the element has any floating descendants whose bottom margin edge is below the element's bottom content edge, then the height is increased to include those edges.

BFC的height:auto(默认值是auto)时如何计算高度:如果元素有任何浮动子体,其下边距边缘位于元素的下内容边缘之下,则会增加高度以包含这些边缘。

翻阅CSS2.1的规范后我们找到了三个特性的来源

块盒、块级盒、块容器

这3个名词的定义在CSS display 3找得到

块盒(block box):既是 块级盒 又是 块级容器

块级盒(block-level box) :在块布局里,表现为独占一行垂直排列的东西如<div>、<p>、display:block/table/flex,就是块级的。

块容器(block container):

容器是参与布局的,比如弹性容器(flex container,即display:flex的元素)参与弹性布局。那么块容器就是参与了块布局的容器了

规范指出,如果它内部只有块级盒,就继承或者新建块格式化上下文,如果它内部只有内联盒,就会生成新的内联格式化上下文,生成内联太牵强,有写手不满issues/1553

那又有块级又有内联级子项怎么办,内联级子项会被匿名块盒生成的的内联格式化上下文包住。这就是最开头我们提到的格式化上下文的共存

规范还说inline-block居然能同时开两个格式化上下文,不知道想给它开什么挂

css-display-3 Note:A block container box can both establish a block formatting context andan inline formatting context simultaneously.

总之spec那头就歪得难懂,这种庞大的文档也是确实不好维护吧,理解万岁

即在块格式化上下文内部,内联元素由匿名的块生成的内联格式化上下文管,这和BFC会垂直排列子项并不冲突

引用CSS Display 3:

A block container either contains only inline-level boxes participating in an inline formatting context, or contains only block-level boxes participating in a block formatting context (possibly generating anonymous block boxes to ensure this constraint, as defined in CSS2§9.2.1.1).
A block container that contains only inline-level content establishes a new inline formatting context. The element then also generates a root inline box which wraps all of its inline content. Note, this root inline box concept effectively replaces the "anonymous inline element" concept introduced in CSS2§9.2.2.1.
A block container establishes a new block formatting context if its parent formatting context is not a block formatting context; otherwise, when participating in a block formatting context itself, it either establishes a new block formatting context for its contents or continues the one in which it participates, as determined by the constraints of other properties (such as overflow or align-content).
Note: A block container box can both establish a block formatting context and an inline formatting context simultaneously.

是块容器(block container)但不是块级盒(block-level box)的例子

文档提到有inline-blocks, table-cells... 它们是部分会建立了新BFC的东西

拿inline-block举例,它是内联级盒不是块级盒(水平排列,不独占一行),所以自身存在于内联格式化上下文中

根据前文提到的规范,一个框要么建立一个新的独立格式上下文,要么继续其包含块的格式上下文(承接内联然后水平排?当然行不通):那就只有新建一个块独立格式上下文了。所以这些是块容器但不是块盒的元素会新建BFC

是块级盒(block-level box),但不是块容器(block container)的例子

文档提到例子有flex container / replaced elements。拿flex container解释:因为flex container是参与弹性布局的,而block container是参与流式布局的。

这些单词前面都带block,所以这些前提都要在块布局(或者说BFC)中。如果是弹性布局规范里那都是flex开头的单词,后文提到

回归正题

到底是flex container开BFC还是flex item开BFC

这里讲Flex,Grid的规范完全一致

CSS flexbox 1
A flex container establishes a new flex formatting context for its contents. This is the same as establishing a block formatting context, except that flex layout is used instead of block layout. For example, floats do not intrude into the flex container, and the flex container’s margins do not collapse with the margins of its contents. Flex containers form a containing block for their contents exactly like block containers do.
翻译: flex container启动新的弹性格式化上下文(Flex Formatting Context),它和BFC相似,但内部采用flex layout弹性布局而非block layout 块布局。
规范里明确指出,一、浮动元素不会侵入flex container,二、不会和子孙发生margin合并。

那么规范里写到了上面这两个BFC特性,还有第三个特性:容纳内部浮动,这里没有提到,因为...flex容器里不能出现浮动子元素

块格式化上下文的特性在弹性格式化上下文里也满足,故我们可以强行理解为flex formatting context和BFC是同一个东西

CSS flexbox 1
A flex item establishes an independent formatting context for its contents. However, flex items themselves are flex-level boxes, not block-level boxes: they participate in their container’s flex formatting context, not in a block formatting context.
翻译: flex item会建立新的独立格式化上下文,但他们自己是flex-level boxes而不是block-level boxes因为它们在flex formatting context里

很好理解,外层是flex布局了,flex item肯定不会承接这个弹性布局,故应当开上下文。其实和inline-block原理是一样

所以结论是,container和item都会开新的"BFC"

flex formatting context能不能被称作BFC(block formatting context)

CSS display 3

BFC
Abbreviation for block formatting context or block formatting context root. Has various informal definitions referring to boxes which contain internal floats, exclude external floats, and suppress margin collapsing, and may therefore refer specifically to one of:
- a block container that establishes a new block formatting context for its contents
- a block box (i.e. a block-level block container) that establishes a block formatting context for its contents (as distinguished from a block box which does not)
- (very loosely) any block-level box that establishes a new formatting context (other than an inline formatting context)

BFC是块格式上下文或块格式上下文根(这个单词叫 block formatting context root,即开启了BFC的那个盒子自己)的缩写。有各种非正式定义,涉及包含内部浮动、排除外部浮动和抑制边距折叠(规范里已经把这三个特点列的明明白白)的框

这里有一个very loosely的定义,关于这个我也和那些人说了issues/5143,但是没有和解。

网上的文章写手们为了把文章写好,当然要对复杂的名词作一个简单的解释。这一点写规范那些人圈地自萌了,坚持说这两个不是同一个东西

个人结论:从CSS学习者的角度讲强行将flex formatting context称为BFC是合理的

流言

对于下面这个结论,应该是在各博里被复制粘贴烂了:

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

要知道规范里明明白白写的只有3个特点!让我们康康另外3个是哪里变出来的

1&3:内部的盒会在垂直方向一个接一个排列。每个元素的margin box的左边,与容器块border box的左边相接触(对于从左往右的格式化,否则相反)。即使存在浮动也是如此(?)

这些是废话,上面这种定义讲出来是没意义的,都是水货

还有一点就是这种话说出来会让人混淆BFC和block formatting context的概念,既然你说垂直方向排:

为什么flexbox开启了BFC但它是横竖都能排,为什么Grid是表格式地排(前文讲到因为开的是flex formatting box)

为什么如果块里存在内联元素内联元素会水平排?(前文讲到共存

这下就引出了更多问题

4:BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素,反之亦然(错误)

规范讲它只影响布局,没有讲这条,那么下面给大家看两个隔离的例子:

19c4a437a6506c77ae5273f742992940.png
粉色是一个inline-block的子元素,这里只改变粉色元素的宽度后,父元素被横向撑开,导致右侧浮动被下移,布局变化,文档高度增加

e772d471eb32256695d098790d890ab7.png
蓝色是一个flex item处于flex container生成的BFC

8d8786a7e33bef70b27fb3bcbe227da6.png
增加蓝色块的高度后,浮动被换行,布局改变

肉眼可见的完全不影响外面的元素 :

2041d7a8c2e73d85c35a5ef2cb75ab67.png
事实上这是写本文的很重要的导火索

2&5&6:margin collapse发生在同一个BFC内部 计算BFC的高度时,考虑BFC所包含的所有元素,连浮动元素也参与计算 浮动盒区域不叠加到BFC上(正确)

规范提到的三个特点

总结-关于BFC你应该记的

!主流启动方式请转MDN,常用overflow:auto/hidden,未来display:flow-root

!flex container和flex item都会新建"BFC"

!手动开启BFC并不是想要换布局,但由于生成了独立的布局区域,这个盒子本身多了一些特性。实例请回头参考引言。

  • 包含内部浮动 (height:auto)
  • 排除外部浮动
  • 抑制边距折叠

!BFC并不是一种防止重排或者性能优化的手段,除非把根的宽高打死或者绝对定位,内部元素仍然能影响外部内容,没有隔离!

emmmm

如果想了解display:flow-root和clearfix相关,可以上css-tricks搜一下https://css-tricks.com/snippets/css/clear-fix/ & https://css-tricks.com/display-flow-root/

我最先是从 https://juejin.im/post/59b73d5bf265da064618731d 看的,里面就是废话太多,还提到什么容器里面的子元素不会影响到外面的元素这种不知道从哪里贴过来的错误,而且引的规范英文和中文翻译压根对不上啊宁不会翻文档别去翻啊...当然也就是感慨下,毕竟别人能高赞说明写的好。

20年5月更新:有空查看了知乎和碧站的HTML,发现flex的确主流了,可以很好替代inline-block作为水平排列元素的方案。果然,2077年,被灵魂发问到如何实现两栏布局,直接flex甩脸上就好了,特别是对多端应用开发友好

其它你不想听的东西和名词解释

盒(box)

盒box是由元素element生成的东西,比如一个div元素就会生成一个原盒principle box

如果是list item列表项,除了principle box还会生成另一个盒

(e.g. display: list-item) generate more than one box (e.g. a principal block box and a child marker box

关于inline-block开启BFC

前面(block container部分)说inline-block如果只承担内联元素会启动内联上下文,有人无奈反对,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值