拒絕水貨 | 除了解決邊距重疊,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)
相關文章
相關標籤/搜索