來源:那些年我們一起清除的浮動
CSS|Float知多少(微信)
問題引起是2016IFE春季問題的任務三,總的父元素parent包含三個浮動的子元素,容器的高度不能自動伸長以適應內容的高度,出現了高度坍塌問題。
代碼如下:
<div class="parent"> <div class="left"></div> <div class="center"></div> <div class="right"></div> </div> .parent{ margin:20px; background-color:red; border:solid 2px black; } .left,.right,.center{ float:left; width:200px; height:200px; background-color:yellow; border:solid 2px blue; }
(最上面的一條黑線就是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。
該方法源自於: How To Clear Floats Without Structural Markup
最后精簡的代碼如下:
.clearfix:after {content:"."; 生成內容作為最后一個元素,至於content里面是點還是其他都是可以的
display:block; 使生成的元素以塊級元素顯示,占滿剩余空間;
height:0; 避免生成內容破壞原有布局的高度。
visibility:hidden; 使生成的內容不可見,並允許可能被生成內容蓋住的內容可以進行點擊和交互;
clear:both; 閉合浮動
}
.clearfix { *zoom:1; } 觸發IE hasLayout。
小結
通過對比,我們不難發現,其實以上列舉的方法,無非有兩類:
其一,通過在浮動元素的末尾添加一個空元素,設置 clear:both屬性,after偽元素其實也是通過 content 在元素的后面生成了內容為一個點的塊級元素;
其二,通過設置父元素 overflow 或者display:table 屬性來閉合浮動,我們來探討一下這里面的原理。
原理就是 Block formatting contexts (塊級格式化上下文),以下簡稱 BFC,它是CSS2.1規范定義的,是頁面 CSS 視覺渲染的一部分,用於決定塊盒子的布局及浮動相互影響范圍的一個區域。簡單來說,BFC 就是一種屬性,這種屬性會影響着元素的定位以及與其兄弟元素之間的相互作用。
下面的情況會產生新的BFC:
- 根元素或其它包含它的元素
- 浮動 (元素的 float 不為 none)
- 絕對定位元素 (元素的 position 為 absolute 或 fixed)
- 行內塊 inline-blocks (元素的 display: inline-block)
- 表格單元格 (元素的 display: table-cell,HTML表格單元格默認屬性)
- 表格標題 (元素的 display: table-caption, HTML表格標題默認屬性)
- overflow 的值不為 visible的元素
- 彈性盒 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的 特性
- 內部的盒會在垂直方向一個接一個排列(可以看作BFC中有一個的常規流);
- 處於同一個BFC中的元素相互影響,可能會發生外邊距疊加,如果這兩個相鄰的塊框不屬於同一個塊級格式化上下文,那么它們的外邊距就不會疊加。;
- 每個元素的margin box的左邊,與容器塊border box的左邊相接觸(對於從左往右的格式化,否則相反)。即使存在浮動也是如此;
- BFC就是頁面上的一個隔離的獨立容器,容器里面的子元素不會影響到外面的元素,反之亦然;
- 計算BFC的高度時,考慮BFC所包含的所有元素,連浮動元素也參與計算;
- 浮動盒區域不疊加到BFC上;
BFC包含創建該上下文元素的所有子元素,但不包括創建了新BFC的子元素的內部元素,也就是一個元素不能同時存在於兩個BFC中。
通俗地來說:創建了 BFC的元素就是一個獨立的盒子,里面的子元素不會在布局上影響外面的元素,反之亦然,同時BFC仍然屬於文檔中的普通流。
從實際代碼來分析BFC
實例一
<style> * { margin: 0; padding: 0; } .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; } .box{ background:#888; height: 100%; margin-left: 50px; } </style> <div class='box'> <div class='left'> </div> <div class='right'> </div> </div>
顯示效果:
綠色框('#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下顯示的bug。 hasLayout屬性不能直接設定,通過設定一些特定的css屬性來觸發並改變 hasLayout 狀態。
元素hasLayout而導致的問題其實一般都很容易發現:往往是內容出現錯位甚至完全不可見。 如:當一個元素內含浮動或絕對定位的內容時,它通常會表現出奇怪和錯誤的行為
一般如果是因為layout而引起的顯示不符期望效果的話,在ff下會表現正常,而在ie下會出現錯誤。這個時候可以嘗試觸發父容器及其中的子容器的haslayout屬性,通常可以通過加上zoom: 1;來調試。直到找到了產生問題的元素,再進行針對性的修正。最好的辦法是對這個元素設置尺寸屬性。但是,有時不便指定尺寸屬性的情況下,就只能尋找替代方案了。對於ie7 ,最好的辦法是設置最小高度屬性為0;這個技術是無害的,因為0本來就是這個屬性的初始值。而且沒有必要對其他瀏覽器隱藏這個屬性。而對於ie6和更早版本中觸發一個元素hasLayout的方法是在overflow屬性是visible的情況下設置這個元素的高度屬性為1%,然后對其他瀏覽器隱藏這個設置。這種技術就是著名的Holly hack。
觸發hasLayout的條件:
- position: absolute
- float: left|right
- display: inline-block
- width: 除 “auto” 外的任意值
- height: 除 “auto” 外的任意值 (例如很多人閉合浮動會用到 height: 1% )
- zoom: 除 “normal” 外的任意值
- writing-mode: tb-rl
在 IE7 中,一些額外的屬性也可以觸發該屬性:
-
min-height: (任何值)
max-height: (任何值除了none)
min-width: (任何值)
max-width: (任何值除了none)
- overflow: hidden|scroll|auto ( 這個屬性在IE之前版本中沒有觸發 layout 的功能。 )
- overflow-x|-y: hidden|scroll|auto (CSS3 盒模型中的屬性,尚未得到瀏覽器的廣泛支持。他們在之前IE版本中同樣沒有觸發 layout 的功能)
綜上所述:
在支持BFC的瀏覽器(IE8+,firefox,chrome,safari)通過創建新的BFC閉合浮動;
在不支持 BFC的瀏覽器 (IE6-7),通過觸發 hasLayout 閉合浮動。