1. 程式人生 > >高度坍塌問題--BFC模式解析

高度坍塌問題--BFC模式解析

  問題引起是2016IFE春季問題的任務三,總的父元素parent包含三個浮動的子元素,容器的高度不能自動伸長以適應內容的高度,出現了高度坍塌問題。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>高度坍塌問題-BFC模式解析</title>
    <style>
        .parent{
            margin:20px;
            background-color
: red
; border: 2px solid black; }
.left,.center,.right{ float: left; width: 200px; height: 200px; background-color: yellow; border: 2px solid blue; }
</style> </head> <body> <div class
="parent">
<div class="left">left</div> <div class="center">center</div> <div class="right">right</div> </div> </body> </html>

這裡寫圖片描述
(最上面的一條黑線就是parent元素了,可以看到它的高度為0)

   原因就是浮動使子元素脫離文件流,父元素無法感知子元素的存在,而且父元素內部不存在其他處於文件流中的元素,也就表現為高度為0。

既然是由浮動引起的,就用clear屬性清除浮動吧,其實這是錯誤的,clear屬性規定的是元素哪一側不允許有其他浮動元素,但是我們並不是想讓父元素周圍沒有其他浮動元素,而是減少浮動帶來的影響,也就是使浮動元素閉合

先來看看閉合浮動的方法:

 1)新增額外標籤

通過在浮動元素末尾新增一個空的標籤,例如 <div style=”clear:both”></div>,其他標籤br等亦可。

<div class="wrap" id="float1">
  <h2>1)新增額外標籤</h2>
  <div class="main left">.main{float:left;}</div>
  <div class="side left">.side{float:right;}</div>
  <div style="clear:both;"></div>
</div>
<div class="footer">.footer</div>

優點:通俗易懂,容易掌握。
缺點:可以想象通過此方法,會新增多少無意義的空標籤,有違結構與表現的分離,在後期維護中將是噩夢,這是堅決不能忍受的,所以你看了這篇文章之後還是建議不要用了吧。

2)使用 br標籤和其自身的 html屬性

這個方法有些小眾,br 有 clear=“all | left | right | none” 屬性

<div class="wrap" id="float2">
  <h2>2)使用 br標籤和其自身的 html屬性</h2>
  <div class="main left">.main{float:left;}</div>
  <div class="side left">.side{float:right;}</div>
  <br clear="all" />
</div>
<div class="footer">.footer</div>

優點:比空標籤方式語義稍強,程式碼量較少
缺點:同樣有違 結構與表現的分離,不推薦使用

3)父元素設定 overflow:hidden      

通過設定父元素overflow值設定為hidden;在IE6中還需要觸發 hasLayout ,例如 zoom:1;

<div class="wrap" id="float3" style="overflow:hidden; *zoom:1;">
  <h2>3)父元素設定 overflow </h2>
  <div class="main left">.main{float:left;}</div>
  <div class="side left">.side{float:right;}</div>
</div>
<div class="footer">.footer</div>

優點:不存在結構和語義化問題,程式碼量極少
缺點:內容增多時候容易造成不會自動換行導致內容被隱藏掉,無法顯示需要溢位的元素;04年POPO就發現overflow:hidden會導致中鍵失效,這是我作為一個多標籤瀏覽控所不能接受的。所以還是不要使用了

4)父元素設定 overflow:auto 屬性

同樣IE6需要觸發hasLayout,演示和3差不多

優點:不存在結構和語義化問題,程式碼量極少
缺點:多個巢狀後,firefox某些情況會造成內容全選;IE中 mouseover 造成寬度改變時會出現最外層模組有滾動條等,firefox早期版本會無故產生focus等, 請看 嗷嗷的 Demo ,不要使用

5)父元素也設定浮動

優點:不存在結構和語義化問題,程式碼量極少
缺點:使得與父元素相鄰的元素的佈局會受到影響,不可能一直浮動到body,不推薦使用

6)父元素設定display:table
    

優點:結構語義化完全正確,程式碼量極少
缺點:盒模型屬性已經改變,由此造成的一系列問題,得不償失,不推薦使用

7)使用:after 偽元素

需要注意的是
:after是偽元素(Pseudo-Element),不是偽類(某些CSS手冊裡面稱之為“偽物件”),很多閉合浮動大全之類的文章都稱之為偽類,不過csser要嚴謹一點,這是一種態度。

由於IE6-7不支援:after,使用 zoom:1觸發 hasLayout

 .clearfix:after {
 content:"."; 
 /*生成內容作為最後一個元素,至於content裡面是點還是其他都是可以的 */       

display:block;         
使生成的元素以塊級元素顯示,佔滿剩餘空間;

height:0;               
避免生成內容破壞原有佈局的高度。
 visibility:hidden;      
 使生成的內容不可見,並允許可能被生成內容蓋住的內容可以進行點選和互動;

 clear:both;  閉合浮動,
}
    .clearfix{
        zoom:1; /* 相容ie6,觸發IE hasLayout */
    }

    <div class="father clearfix">
        <div class="son"></div>
    </div>

    <div class="next"></div>

8)使用 :before和 :after 雙偽元素

.clearfix:before,.clearfix:after{
    display: table;
    content: "";
}
.clearfix:after {
    clear: both;
}
.clearfix {
    zoom: 1;
}

小結

通過對比,我們不難發現,其實以上列舉的方法,無非有兩類:

其一,通過在浮動元素的末尾新增一個空元素,設定 clear:both屬性,after偽元素其實也是通過 content 在元素的後面生成了內容為一個點的塊級元素;

其二,通過設定父元素 overflow 或者display:table 屬性來閉合浮動,我們來探討一下這裡面的原理。

原理

原理就是 Block formatting contexts(塊級格式化上下文,BFC),是css2.1規範定義的,是頁面css視覺渲染的一部分,用於決定盒子的佈局及浮動相互影響範圍的一個區域。簡單來說,BFC就是一種屬性,會影響著元素的定位以及與其兄弟元素之間的相互作用

下面的情況會產生新的BFC:

  1. 根元素或其他包含它的元素
  2. 浮動(元素的float不為none)
  3. 絕對定位元素(position為absolute或者fixed)
  4. 行內塊(display:inline-block)
  5. 表格單元格(display:table-cell,html表格單元格預設屬性)
  6. 表格標題(display:table-caption,html表格標題預設屬性)
  7. overflow的值不為visible的元素
  8. 彈性盒flex boxes(display:flex或者inline-flex)

其中,最常見的就是overflow:hidden、float:left/right、position:absolute。也就是說,每次看到這些屬性的時候,就代表了該元素已經建立了一個BFC了。

    需要注意的是,display:table 本身並不會建立BFC,但是它會產生匿名框(anonymous boxes , 沒有名字不能被選擇器選中的盒,它們的所有屬性都為inherit或初始預設值;),而匿名框中的display:table-cell可以建立新的BFC,換句話說,觸發塊級格式化上下文的是匿名框,而不是display:table。所以通過display:table和display:table-cell建立的BFC效果是不一樣的。(目前沒有懂????)
 

BFC的 特性

  1. 內部的盒會在垂直方向一個接一個排列(可以看作BFC中有一個的常規流);
  2. 處於同一個BFC中的元素相互影響,可能會發生外邊距疊加,如果這兩個相鄰的塊框不屬於同一個塊級格式化上下文,那麼它們的外邊距就不會疊加。;
  3. 每個元素的margin box的左邊,與容器塊border box的左邊相接觸(對於從左往右的格式化,否則相反)。即使存在浮動也是如此;
  4. BFC就是頁面上的一個隔離的獨立容器,容器裡面的子元素不會影響到外面的元素,反之亦然;
  5. 計算BFC的高度時,考慮BFC所包含的所有元素,連浮動元素也參與計算;
  6. 浮動盒區域不疊加到BFC上;

    BFC包含建立該上下文元素的所有子元素,但不包括建立了新BFC的子元素的內部元素,也就是一個元素不能同時存在於兩個BFC中。

    通俗地來說:建立了 BFC的元素就是一個獨立的盒子,裡面的子元素不會在佈局上影響外面的元素,反之亦然,同時BFC仍然屬於文件中的普通流。

從實際程式碼來分析BFC
例項解析BFC

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>高度坍塌問題-BFC模式解析</title>
    <style>
         * {
        margin: 0;
        padding: 0;
    }
    .box{
        background:#888;  /* 灰色 */
        height: 100%;
        margin-left: 50px;
    }
    .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;
    }

    </style>
</head>
<body>
    <div class='box'>
        <div class='left'></div>
        <div class='right'></div>
    </div>
</body>
</html> 

這裡寫圖片描述

綠色框(’#left’)向左浮動,它建立了一個新BFC,但暫時不討論它所建立的BFC。由於綠色框浮動了,它脫離了原本normal flow的位置,因此,粉色框(’#right’)就被定位到灰色父元素的左上角(特性3:元素左邊與容器左邊相接觸),與浮動綠色框發生了重疊。

  同時,由於灰色框(’#box’)並沒有建立BFC,因此在計算高度的時候,並沒有考慮綠色框的區域,發生了高度坍塌,這也是常見問題之一。
 例項二:
 
現在通過設定overflow:hidden來建立BFC閉合浮動,再看看效果如何。

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

這裡寫圖片描述
灰色框建立了一個新的BFC後,高度發生了變化,計算高度時它將綠色框區域也考慮進去了(特性5:計算BFC的高度時,浮動元素也參與計算);

而綠色框和紅色框的顯示效果仍然沒有任何變化。
例項三
現在,現將一些小塊新增到粉色框中,看看效果:

<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>

這裡寫圖片描述
由於粉色框沒有建立新的BFC,因此粉色框中白色塊受到了綠色框的影響,被擠到了右邊去了(特性6:浮動盒區域不疊加到BFC上)。先不管這個,看看白色塊的margin,發生了外邊距疊加。

例項四:
利用同例項二中一樣的方法,為粉色框建立BFC,防止與浮動元素(綠色框)重疊:

<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>

這裡寫圖片描述
 一旦粉色框建立了新的BFC以後,粉色框就不與綠色浮動框發生重疊了,同時內部的白色塊處於隔離的空間(特性4:BFC就是頁面上的一個隔離的獨立容器),白色塊也不會受到綠色浮動框的擠壓。

例項總結

  以上就是BFC的分析,BFC的概念比較抽象,但通過例項分析應該能夠更好地理解BFC。在實際中,利用BFC可以閉合浮動(例項二),防止與浮動元素重疊(例項四)。同時,由於BFC的隔離作用,可以利用BFC包含一個元素,防止這個元素與BFC外的元素髮生margin collapse。
  

那麼到底為什麼坍塌?

  float 脫離了普通流,並且建立了新的BFC,而父元素不具備產生 BFC 的條件,所以它的高度為0。

如何解決?

  通過了解BFC的特性我們知道,BFC會把它包含的浮動元素高度也算在裡面,也就是閉合浮動
  拿 overflow: auto 舉例:overflow: auto 並不會閉合浮動,而是 overflow: auto 會建立一個新的BFC,避免浮動的元素侵入其他元素。

  IE6-7有一個特有的屬性就是haslayout,當一個元素的hasLayout屬性值為true時,我們說這個元素有一個佈局(layout),它負責對自己和可能的後代元素進行尺寸計算和定位,當屬性值為false時,它的尺寸和位置由最近擁有佈局的祖先元素控制。

  很多情況下,把 hasLayout的狀態改成true 可以解決很大部分ie下顯示的bughasLayout屬性不能直接設定,通過設定一些特定的css屬性來觸發並改變 hasLayout 狀態

  元素hasLayout而導致的問題其實一般都很容易發現:往往是內容出現錯位甚至完全不可見。 如:當一個元素內含浮動或絕對定位的內容時,它通常會表現出奇怪和錯誤的行為。

  一般如果是因為layout而引起的顯示不符期望效果的話,在ff下會表現正常,而在ie下會出現錯誤。這個時候可以嘗試觸發父容器及其中的子容器的haslayout屬性,通常可以通過加上zoom: 1;來除錯。直到找到了產生問題的元素,再進行鍼對性的修正。最好的辦法是對這個元素設定尺寸屬性。但是,有時不便指定尺寸屬性的情況下,就只能尋找替代方案了。
  對於ie7 ,最好的辦法是設定最小高度屬性為0;這個技術是無害的,因為0本來就是這個屬性的初始值。而且沒有必要對其他瀏覽器隱藏這個屬性。
  而對於ie6和更早版本中觸發一個元素hasLayout的方法是在overflow屬性是visible的情況下設定這個元素的高度屬性為1%,然後對其他瀏覽器隱藏這個設定。這種技術就是著名的Holly hack。

觸發hasLayout的條件:

  1. position: absolute
  2. float: left|right
  3. display: inline-block
  4. width: 除 “auto” 外的任意值
  5. height: 除 “auto” 外的任意值 (例如很多人閉合浮動會用到 height: 1% )
  6. zoom: 除 “normal” 外的任意值
  7. writing-mode: tb-rl
    在 IE7 中,一些額外的屬性也可以觸發該屬性:

  8. min-height: (任何值)

  9. max-height: (任何值除了none)
  10. min-width: (任何值)
  11. max-width: (任何值除了none)
  12. overflow: hidden|scroll|auto ( 這個屬性在IE之前版本中沒有觸發 layout 的功能。 )
  13. overflow-x|-y: hidden|scroll|auto (CSS3 盒模型中的屬性,尚未得到瀏覽器的廣泛支援。他們在之前IE版本中同樣沒有觸發 layout 的功能)

綜上所述:

在支援BFC的瀏覽器(IE8+,firefox,chrome,safari)通過建立新的BFC閉合浮動;

在不支援 BFC的瀏覽器 (IE6-7),通過觸發 hasLayout 閉合浮動。