拒绝水货 | 除了解决边距重叠,BFC还能够这么用

大家都不看的总集篇: 从零开始的大前端筑基之旅(深刻浅出,持续更新~)
以为不错就顺手点个赞吧~css

划重点: 本文全部示例均为手打html示例,能够直接选中示例在控制台调试,方便验证各类想法。手敲不易,若是你以为示例调试方便,那我在此求个赞~html

外边距重叠

外边距重叠就是 margin-collapse。相邻的两个盒子(多是兄弟关系也多是祖先关系)的外边距能够结合成一个单独的外边距。 这种合并外边距的方式被称为折叠,结合而成的外边距称为折叠外边距。前端

折叠结果遵循下列计算原则:react

  • 两个相邻的外面边距是正数时,折叠结果就是他们之中的较大值;
  • 两个相邻的外边距都是负数时,折叠结果是二者绝对值的较大值;
  • 两个外边距一正一负时,折叠结果是二者的相加的和;

有些人看到这里可能会想:啊,我作列表的时候遇到过这种状况,折腾了我半天呢。webpack

另外一些人可能会想:这是什么玩意,我怎么没遇到过?这难道不是css的bug么?css3

咱们先来讲说什么状况下不会产生外边距重叠web

  1. 水平边距永远不会重合。
  2. 相邻的盒模型中,若是其中的一个是浮动的(float),垂直margin不会重叠,而且浮动的盒模型和它的子元素之间也是这样。
  3. 设置了overflow属性的元素和它的子元素之间的margin不被重叠(overflow取值为visible除外)。
  4. 设置了绝对定位(position:absolute)的盒模型,垂直margin不会被重叠,而且和他们的子元素之间也是同样。

等等,先不要列举了,这些条件怎么看着那么眼熟呢?嗯。。。这不就是生成BFC的条件么。。。。typescript

什么是BFC?

BFC(Block formatting context)直译为"块级格式化上下文"。它是一个独立的渲染区域,只有Block-level box参与, 它规定了内部的Block-level Box如何布局,而且与这个区域外部绝不相干。segmentfault

咱们常说的文档流其实分为定位流、浮动流、普通流三种。而普通流其实就是指BFC中的FC。FC(Formatting Context),直译过来是格式化上下文,它是页面中的一块渲染区域,有一套渲染规则,决定了其子元素如何布局,以及和其余元素之间的关系和做用。babel

先介绍下Box、Formatting Context的概念。

Box:css布局的基本单位

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 中才有, 这儿先不讲了。

Formatting Context

Formatting context 是 W3C CSS2.1 规范中的一个概念。它是页面中的一块渲染区域,而且有一套渲染规则,它决定了其子元素将如何定位,以及和其余元素的关系和相互做用。最多见的 Formatting contextBlock fomatting context (简称BFC)和 Inline formatting context (简称IFC)。

具备 BFC 特性的元素能够看做是隔离了的独立容器,容器里面的元素不会在布局上影响到外面的元素,而且 BFC 具备普通容器所没有的一些特性。

BFC的布局约束规则

  • 内部的Box会在垂直方向,一个接一个地放置。

  • Box垂直方向的距离由margin决定。属于同一个BFC的两个相邻Box的margin会发生重叠。

  • 每一个盒子(块盒与行盒)的margin box的左边,与包含块border box的左边相接触(对于从左往右的格式化,不然相反)。即便存在浮动也是如此。即BFC中子元素不会超出他的包含块。

  • BFC的区域不会与float box重叠。

  • BFC就是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面的元素。反之也如此。

  • 计算BFC的高度时,浮动元素也参与计算。

BFC触发方式

  1. 根元素,即HTML标签
  2. 浮动元素:float值为left、right
  3. overflow值不为 visible,为 auto、scroll、hidden
  4. display值为 inline-block、table-cell、table-caption、table、inline-table、flex、inline-flex、grid、inline-grid
  5. 定位元素:position值为 absolute、fixed 注意: display: table也能够生成BFC的缘由在于Table会默认生成一个匿名的table-cell,是这个匿名的table-cell生成了BFC。

BFC做用

BFC是页面上的一个隔离的独立容器,容器里面的子元素不会影响到外面元素,反之亦然。咱们能够利用BFC的这个特性来作不少事。

1. 同一个 BFC 下外边距会发生折叠

<div class="container">
  <div class="inner"></div>
  <div class="inner"></div>
</div>
复制代码
.container{
  display: inline-block;
  border: 1px solid grey;
}
.inner{
  width: 20px;
  height: 20px;
  margin: 20px;
  border: 1px solid grey;
  background: lightblue;
}
复制代码

从效果上看,由于两个 div 元素都处于同一个 BFC 容器下 (这里经过设置containerdisplay: inline-block ) 因此第一个 div 的下边距和第二个 div 的上边距发生了重叠,因此两个盒子之间距离只有 20px,而不是 40px。

首先这不是 CSS 的 bug,咱们能够理解为一种规范,若是想要避免外边距的重叠,能够将其放在不一样的 BFC 容器中。

<div class="container">
  <div class="inner"></div>
  <div class="container1">
     <div class="inner"></div>
  </div>
</div>
复制代码
.container{
  display: inline-block;
  border: 1px solid grey;
}
.container1{
  display: inline-block;
}
.inner{
  width: 20px;
  height: 40px;
  margin: 40px;
  border: 1px solid grey;
  background: orange;
  overflow: hidden;
}
复制代码

这里,将第二个div放进container1中,经过设置containerdisplay: inline-block使其产生BFC,这样,两个inner就在不一样的BFC下,边距就不会重叠。两个盒子之间距离是 40px。

ps:此样例使用html编写,可直接点击示例在控制台调试,试试取消container1的style以后的效果吧

2. BFC 能够包含浮动的元素(清除浮动)

浮动的元素会脱离普通文档流,来看下下面一个例子

<div class="container">
    <div class="inner"></div>
</div>
复制代码
.container{
  border: 1px solid grey;
}

.inner{
  width: 20px;
  height: 20px;
  margin: 20px;
  background: orange;
  float: left;
}
复制代码

栗子,包括下面的横线

因为容器内元素浮动,脱离了文档流,因此容器只剩下 2px 的边距高度。若是使触发容器的 BFC,那么容器将会包裹着浮动元素。

经过overflow: hidden使container生成BFC

.container{
  border: 1px solid grey;
  overflow: hidden;
}
复制代码

本例也能够直接点击调试哦~

3. BFC 能够阻止元素被浮动元素覆盖

你真的清楚浮动效果么?告诉你个浮动的隐藏效果,做为交换,你给我点个赞,咋样

解释这个做用以前,我先来解释一下什么是浮动

什么是浮动

元素加了浮动后,会脱离文档流,提高了半层层级,向着指定方向移动,直到遇到父元素的边界或另外一个浮动元素中止

什么是层级

若是将整个元素看作一层,则下半层是元素自己(背景样式等),上半层是元素中的内容

举例

<div class="container">
  <div class="box1">box1</div>
   <div class="box2">box2</div>
   <div class="box3">box3</div>
</div>
复制代码
.container{
 width: 40px;
 border: 1px solid black;
}
.container div{
  width: 100%;
  height: 30px;
}
.box1 {
  background: yellow;
}
.box2 {
    background: orange; 
}
.box3 {
     background: pink; 
}
复制代码

三个盒子都没有浮动时

box1
box2
box3

当给box2添加float:left时,三个盒子的排列变成

box1
box2
box3

此时因为box2浮动脱离了文档流,box3上移,被box2遮挡了。但此时box3盒子里的文字box3并无上移!!!

小知识

  1. 浮动元素之间是漂浮着,并不会造成一个流。浮动元素老是要保证本身的顶部与上一个标准流中的元素(未浮动元素)的底部对齐。
  2. position:absolutefloat会隐式地改变display类型,除display:none外,只要设置了position:absolutefloat,都会让元素以display:inline-block的方式显示,能够设置长宽

浮动效果讲完了,咱们来看看元素被浮动元素覆盖的例子

<div class="container">
  <div class="left">我是一个左浮动的元素</div>
  <div class="right">我是一个没有设置浮动, 
也没有触发 BFC 的元素, 个人一部分被覆盖了</div>
</div>
复制代码
.left{
  width: 100px;
  height: 50px;
  background: orange;
  float: left;
}
.right{
  width: 200px;
  height: 200px;
  background: lightblue;
}
复制代码
我是一个左浮动的元素
我是一个没有设置浮动, 也没有触发 BFC 的元素, 个人一部分被覆盖了

第二个元素有部分被浮动元素所覆盖,(可是文本信息不会被浮动元素所覆盖,缘由在上面浮动例子解释过) 若是想避免元素被覆盖,可触第二个元素的 BFC 特性,在第二个元素中加入 overflow: hidden,就会变成:

我是一个左浮动的元素
我是一个没有设置浮动, 可是触发 BFC 的元素, 我又完整了

这个方法能够用来实现两列自适应布局,经过设置container的宽度,去掉right的宽度便可。这时候左边的宽度固定,右边的内容自适应宽度。

Ps: 建议在控制台调试一下本示例

ps:我通常使用display: flex,而后左侧宽度固定,右侧设置flex: 1,这个方法垂直方向也可使用,我经常使用来设置滚动列表的高度,这个方法也能够实现动态宽度的布局

例子以下:

<div class="container">
  <div class="left">左侧元素,定宽</div>
  <div class="right">右侧元素,动态填充剩余空间</div>
</div>
复制代码
.container{
  width: 400px;
  display: flex;
  border: 1px solid grey;
}

.left{
  width: 100px;
  height: 50px;
  background: orange;
}
.right{
  height: 200px;
  flex: 1;
  background: lightblue;
}
复制代码
左侧元素,定宽
右侧元素,动态填充剩余空间

你能够在控制台调整一下container的宽度,右侧元素会自动填充

外边距重叠示例

讲完了BFC及其做用,除了最直观的,同一个 BFC 下外边距会发生折叠,咱们来看下其余边距重叠的示例

1. 当一个元素包含在另外一个元素之中时,子元素与父元素之间也会产生重叠现象,重叠后的外边距,等于其中最大者

栗子以下:

<div class="container">
  <div class="title">
        此部分是能更容易看出让下面的块的margin-top。
  </div>
  <div class = "content">
    <div class="inner">
      子元素
      margin-top:20px;
    </div>
    <h2>父元素</h2>
    没有设置margin-top
  </div>
</div>
复制代码
.container{
  width: 400px;
  border: 1px solid grey;
}
.title{
  height:50px;
  background: #eee;
 }
.content{
  height:200px;
  background: #88f;
}
.inner{
  height:100px;
  margin-top:20px;
  background: #0ff;
  width:200px;
}
复制代码
此部分是能更容易看出让下面的块的margin-top。
子元素 margin-top:20px;

父元素

没有设置margin-top

父元素和子元素margin-top发生了重叠,最终结果至关于父元素设置了20px的margin-top。

能够设置 overflow:hidden来解决这个问题。你能够选中示例在控制台本身尝试一下其余解决方案。

2. 若是一个无内容的空元素,其自身上下边距也会产生重叠。

<div class="container">
  <div class="inner"></div>
</div>
复制代码
.container{
  width: 200px;
  border: 1px solid grey;
}
.inner{
  margin-top:20px;
  margin-bottom: 20px;
}
复制代码

这个框的高度只有20px,稍微改动下inner元素,好比加个文字,或者加个边框,都会破坏这种现象,欢迎本身打开控制台尝试一下各类解决方案。

好了,外边距重叠、BFC、及浮动的讲解到此结束。有任何想法和意见欢迎在评论中指出。若是你收获了新知识,请点个赞告诉我~

本文收纳于: 从零开始的大前端筑基之旅(深刻浅出,持续更新~)

推荐阅读:

参考文档:

  1. 什么是BFC?看这一篇就够了
  2. 10 分钟理解 BFC 原理
  3. CSS中重要的BFC
  4. CSS 外边距(margin)重叠及防止方法边距重叠解决方案(BFC)
相关文章
相关标签/搜索