Margin Collapse、BFC外加一点点“清除浮动”

建议在PC端阅读
本文面向对象:对标题中的概念基本不了解或仅仅听说过名字的人。如果已有一定了解请直接拉到最下看推荐阅读
另外其实这是个很大的话题,这里仅当抛砖引玉,并提供进一步阅读的文章

一些demo

我们先从一些现象开始入手吧↓(建议在另一窗口打开示例,一边修改示例一边阅读效果更佳)
本文示例

例一

第一个例子是最常见的一种边界折叠(Margin Collapse)。一般我们很容易忽略掉这个现象,因为它和我们大多数情况下的直觉比较符合。
例如第一个例子里的两个p标签,它们的关系是并列的,所以我们设置margin:20px 0;时心里其实是想让两个p标签之间相隔20px(事实上现在也是这样表现的)。
但有时候我们并不想让它们折叠(我们希望“修正”的同时,保持使用margin并尽量少添加难以理解的CSS属性),那要怎么做呢?(即需要清楚产生边界折叠的条件)

例子中提示的做法是用一个设置了overflow:hidden;的div将其包裹,但是overflow:hidden;作用明明是剪裁溢出的内容,为何能产生取消边界折叠的效果呢?直接给出这样的解决方案让人难以理解而且容易忘记。

(似乎只能通过外加BFC创造不同的BFC来避免...)

例二

如果说第一个例子一定程度上还算符合直觉,那第二个例子的情况老实说我是不能理解的...一度让我以为是出了bug...但了解了Margin Collapse的产生条件之后,相信你能明白为何会出现这种现象以及如果消除它。

例三

第三个例子主要和BFC有关。一般说到"清除浮动"就会想到clear:both; overflow:hidden;之类.但是给父元素设置浮动居然也能"清除浮动"???估计深受浮动折磨的人看到这现象会进一步崩溃:为什么这样也能清除浮动?到底有多少种清除浮动的方法?(当然“清除浮动”不是本文重点,若想进一步了解请看推荐阅读


所以这些现象到底跟标题中的Margin Collapse、BFC有什么关系呢?

上面例子的现象你都能给出解释和解决方案吗?如果都没问题的话恭喜你,可以直接下拉到最后看推荐阅读进行进一步阅读了。如果一头雾水的话也没关系,我们一步一步来看是怎么回事。

想一下,有没有觉得这有点像读英语时每个单词都认识,连在一起就看不懂的情况?这是因为缺乏相关的语法知识(当然也有可能是缺少上下文)
每一条CSS Rule就像一个个单词,而相应的语法就是w3c制定的布局方案了(当然这里的类比不完全准确,自然语言是与上下文有关的语法,而CSS是与上下文无关的语法)

那w3c是怎么规定Margin Collapse的呢?


产生折叠的必备条件:margin必须是邻接的!

而根据w3c规范,两个margin是邻接的必须满足以下条件:
1.必须是处于常规文档流(非float和绝对定位)的块级盒子,并且处于同一个BFC(BFC产生条件见下文)当中。
2.没有线盒,没有空隙(clearance,本文不涉及),没有padding和border将他们分隔开
3.都属于垂直方向上相邻的外边距,可以是下面任意一种情况

  • 元素的margin-top与其第一个常规文档流的子元素的margin-top(即例二)
  • 元素的margin-bottom与其下一个常规文档流的兄弟元素的margin-top(即例一)
  • height为auto的元素的margin-bottom与其最后一个常规文档流的子元素的margin-bottom
  • 高度为0并且最小高度也为0,不包含常规文档流的子元素,并且自身没有建立新的BFC的元素的margin-top和margin-bottom

以上的条件意味着下列的规则:

  • 创建了新的BFC的元素(BFC元素的产生条件见下文)与它的子元素的外边距不会折叠
  • 浮动元素不与任何元素的外边距产生折叠(包括其父元素和子元素)
  • 绝对定位元素不与任何元素的外边距产生折叠
  • inline-block元素不与任何元素的外边距产生折叠
  • 一个常规文档流元素的margin-bottom与它下一个常规文档流的兄弟元素的margin-top会产生折叠,除非它们之间存在间隙(clearance)。
  • 一个常规文档流元素的margin-top 与其第一个常规文档流的子元素的margin-top产生折叠,条件为父元素不包含 padding 和 border ,子元素不包含 clearance。
  • 一个 'height' 为 'auto' 并且 'min-height' 为 '0'的常规文档流元素的 margin-bottom 会与其最后一个常规文档流子元素的 margin-bottom 折叠,条件为父元素不包含 padding 和 border ,子元素的 margin-bottom 不与包含 clearance 的 margin-top 折叠。
    一个不包含border-top、border-bottom、padding-top、padding-bottom的常规文档流元素,并且其 'height' 为 0 或 'auto', 'min-height' 为 '0',其里面也不包含行盒(line box),其自身的 margin-top 和 margin-bottom 会折叠。

折叠的结果:

两个相邻的外边距都是正数时,折叠结果是它们两者之间较大的值。
两个相邻的外边距都是负数时,折叠结果是两者绝对值的较大值。
两个外边距一正一负时,折叠结果是两者的相加的和。

摘自w3plus: http://www.w3cplus.com/css/understanding-bfc-and-margin-collapse.html © w3cplus.com


BFC(Block Formatting Contexts,块级上下文)的产生条件

BFC在CSS3中改为Flow Root,触发条件多了下面加粗的部分

  • 浮动元素,float 除 none 以外的值
  • 绝对定位元素,position(absolute,fixed
  • display 为以下其中之一的值 inline-blocks,table-cells,table-captions
  • overflow 除了 visible 以外的值(hidden,auto,scroll)

注:"display:table" 本身并不产生 BFC,但它会产生匿名框,匿名框中包含 "display:table-cell" 的框会产成 BFC。


好的,上面一口气丢了一大堆定义和条件出来,估计直接强行读下来已经有点晕了。没关系我们结合文章开头的例子看一下吧。

例子解析

例一

显然例一符合折叠的第一个条件:

1.必须是处于常规文档流(非float和绝对定位)的块级盒子,并且处于同一个BFC(BFC产生条件见下文)当中。

那要取消折叠自然就是破坏第一个条件啦,于是第一反应就是用overflow:hidden;来把p标签变成BFC。然后你就会开心的发现......并没有什么变化。
为什么呢?我们再来读一下第一个条件

"....处于同一个BFC当中。"

嗯,这里要搞清楚一个概念,BFC是一个区域,不是一个元素。
也就是说,你给p标签设的overflow:hidden;只是把p标签里的内容设置成一个新的BFC,但是这个p标签仍处于它之前所在BFC中这个事实没有改变。
所以给出的解决方案是外面多包裹一个div再把这个div变成BFC(如overflow:hidden;),这样被包裹的p标签处于它的父元素创建的BFC,而它的父元素此时和另一个p标签处于同一个BFC,所以被包裹的p标签与另一个p标签自然不处于同一个BFC了(表达能力有限,这段写的有点绕)

虽然要多加一个标签,但其实仔细想想也没有破坏语义性。因为正是多加了一个标签才表示了被包裹的内容与外面的内容不处于同一个上下文。

例二

例二其实也是因为处于同一个BFC中而导致的,你可以试着给.parent加上overflow:hidden;看看效果。demo中让你尝试加border或padding主要是因为这个折叠条件比较容易忽略。
不过产生BFC的条件多多少少都会有副作用,而这里其实只要让子元素margin与父元素marigin不相邻(折叠的最基本要求)就可以了
这里推荐的解决方案是使用伪元素:

.fathet:before{
     content:"";
     display: table;
}

首先content是伪元素生成的必要条件
这里的原理是:display:table;会生成包含display:table-cell;的匿名框(见上文BFC产生条件),从而产生了一个“没有高度”的BFC区域(个人理解,如有错误欢迎指出)将子元素和父元素的margin分割开了。

例三

这里主要是因为BFC的特性所致
BFC有如下特性:

  1. BFC可以包含浮动元素

w3c原文:“'Auto' heights for block formatting context roots”,也就是 BFC 会根据子元素的情况自动适应高度,即使其子元素中包括浮动元素。

  1. BFC可以阻止元素被浮动元素覆盖(即取消文字环绕的效果)

所以例三给父元素设置浮动后高度不再坍塌只是因为父元素的产生了BFC,而用其他产生BFC的方法同样能包含浮动元素。
值得注意的是,虽然BFC有闭合浮动的效果,但并不推荐利用BFC闭合浮动。因为每一个用于产生BFC的条件都有一定程度的副作用,尽管当时可能副作用不明显,但也是个很大的隐患。
推荐的“清除浮动”的方法是Nicolas Gallagher大神提出的micro clearfix hack:

.clearfix:after{
  content:"";
  display:table;
  clear:both;
}

总结

  1. Margin Collapse不是bug,而是在CSS布局规则里面有规定的,具体出现情况见上文。
  2. BFC是一个区域,或者说是元素具有的一个特性,而不是元素本身。
  3. BFC能闭合浮动,但不应该用BFC来闭合浮动。

思考

先给body加上border,方便观察。
试着给body标签加上margin,再给body的第一个子元素(需要是块级的)设置margin,看是否会发生折叠?
如果会发生折叠,也就是符合条件1

1.必须是处于常规文档流(非float和绝对定位)的块级盒子,并且处于同一个BFC当中。

也就是处于同一个BFC中。这时如果给第一个子元素设置浮动又会发生什么呢?

实验的结果是body无法包含浮动元素,这与BFC的特性矛盾。但同时却能发生Margin Collapse,也就是处于同一个BFC中。这似乎有点矛盾。
当然我也没想通这是为什么,如果有明白的人欢迎指教。我能想到的就只有因为body标签太特殊,这是浏览器对它作出的特殊处理......


推荐阅读

四篇文章搞定BFC(建议依次阅读)

  1. 详说 Block Formatting Contexts (块级格式化上下文)
  2. W3C的官方定义
  3. Understanding Block Formatting Contexts in CSS
  4. Formatting contexts

BFC与Margin Collapse

深入理解BFC和Margin Collapse

搞定清除浮动

那些年我们一起清除过的浮动

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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

推荐阅读更多精彩内容

  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 13,728评论 1 92
  • 1.浮动元素有什么特征?对父容器、其他浮动元素、普通元素、文字分别有什么影响? 何谓浮动元素?有什么特征?所谓浮动...
    草鞋弟阅读 809评论 0 1
  • 浮动元素的特征,对父容器、对其他浮动元素、普通元素、文字的影响。 浮动会使应用的元素脱离文档流。按指定的位置来移动...
    邢烽朔阅读 651评论 2 7
  • 1.在什么场景下会出现外边距合并?如何合并?如何不让相邻元素外边距合并?给个父子外边距合并的范例 概念:在CSS当...
    饥人谷_任磊阅读 643评论 0 3
  • 在什么场景下会出现外边距合并?如何合并?如何不让相邻元素外边距合并?给个父子外边距合并的范例 在CSS当中,相邻的...
    Nicklzy阅读 837评论 0 49