簡單說一下 [清除 & 閉合] 浮動

_zon_發表於2018-04-11

本文的起因是團隊現在處於缺人的階段,最近開始幫忙進行電話面試的操作。面了不少了,有不少收穫,其中最大的還是對自己的警惕作用。雖然是自己在面試別人,但也發現,很多在交流過程中涉及到的內容,自己雖然都知道或者有了解,但是要系統或者清晰的描述出來其實是不容易的,所以開始整理一些看似都懂的一些“亂七八糟”的知識。

【本文主要是根據參考資料整理以及一些自己的理解,參考資料已在文末列出。】

浮動是一個CSS佈局中的經典屬性,用過浮動的小夥伴肯定會遇到需要清除浮動或者說閉合浮動的情況。我對清除還是閉合的理解是,看你的實現方法來說。

簡單來說,使用clear屬性來實現可以認為是清除浮動(clearing float),使元素生成BFC或者hasLayout屬性為true的方法可以稱之為閉合浮動(enclosing float)。

CSS中的定位機制可以分為:

  • 普通流(俗稱文件流,但是標準裡面為normal flow,所以稱之為普通流比較合理)。
  • 浮動:引用一絲姐姐的文章中的話如下,需要注意的點是浮動框會一直浮動到包含框或者另一個浮動框為止,並且浮動的元素是脫離普通流的,脫離了之後只會影響普通流中的內聯元素的佈局,對於塊級元素沒有影響,就像這個浮動元素不存在一樣。同時,有可能會出現“高度塌陷”的情況(這也是清除&閉合浮動需求的主要場景)。

浮動的框可以左右移動,直至它的外邊緣遇到包含框或者另一個浮動框的邊緣。浮動框不屬於文件中的普通流,當一個元素浮動之後,不會影響到塊級框的佈局而只會影響內聯框(通常是文字)的排列,文件中的普通流就會表現得和浮動框不存在一樣,當浮動框高度超出包含框的時候,也就會出現包含框不會自動伸高來閉合浮動元素(“高度塌陷”現象)。

  • 絕對定位:不是本文的重點,就不多說了。簡單來說,絕對定位的元素是脫離普通流的,相對於最近的display屬性為非static的父級元素定位。

閉合浮動

閉合浮動的本質就是讓元素產生BFC或者hasLayout屬性為true。

BFC的建立方法(參考自Block formatting context MDN),加粗的為常見或者需要注意的方法:

  • the root element or something that contains it
  • floats (elements where float is not none)
  • absolutely positioned elements (elements where position is absolute or fixed)
  • inline-blocks (elements with display: inline-block)
  • table cells (elements with display: table-cell, which is the default for HTML table cells)
  • table captions (elements with display: table-caption, which is the default for HTML table captions)
  • anonymous table cells implicitly created by the elements with display: table, table-row, table-row-group, table-header-group, table-footer-group (which is the default for HTML tables, table rows, table bodies, table headers and table footers, respectively), or inline-table
  • block elements where overflow has a value other than visible
  • display: flow-root
  • elements with contain: layout, content, or strict
  • flex items (direct children of the element with display: flex or inline-flex)
  • grid items (direct children of the element with display: grid or inline-grid)
  • multicol containers (elements where column-count or column-width is not auto, including elements with column-count: 1)
  • column-span: all should always create a new formatting context, even when the column-span: all element isn't contained by a multicol container (Spec change, Chrome bug).

對於hasLayout屬性,一絲的文章中:

IE6-7的顯示引擎使用的是一個稱為佈局(layout)的內部概念,由於這個顯示引擎自身存在很多的缺陷,直接導致了IE6-7的很多顯示bug。當我們說一個元素“得到 layout”,或者說一個元素“擁有 layout” 的時候,我們的意思是指它的微軟專有屬性 hasLayout http://msdn.microsoft.com/worksh ... rties/haslayout.asp 為此被設為了 true 。IE6-7使用佈局的概念來控制元素的尺寸和定位,那些擁有佈局(have layout)的元素負責本身及其子元素的尺寸設定和定位。如果一個元素的 hasLayout 為false,那麼它的尺寸和位置由最近擁有佈局的祖先元素控制。

簡單來說,就是舊版本IE特有的一個控制佈局的概念,對於我們來說要考慮的是在處理IE的相容性的時候需要留意這個屬性,從表現上來說,可以將hasLayout等同於BFC來考慮。

觸發hasLayout的條件:

  • position: absolute
  • float: left|right
  • display: inline-block
  • width: 除 “auto” 外的任意值
  • height: 除 “auto” 外的任意值 (例如很多人閉合浮動會用到 height: 1% )
  • zoom: 除 “normal” 外的任意值 (MSDN) http://msdn.microsoft.com/worksh ... properties/zoom.asp
  • writing-mode: tb-rl (MSDN) http://msdn.microsoft.com/worksh ... ies/writingmode.asp

在 IE7 中,overflow 也變成了一個 layout 觸發器:

  • overflow: hidden|scroll|auto ( 這個屬性在IE之前版本中沒有觸發 layout 的功能。 )
  • overflow-x|-y: hidden|scroll|auto (CSS3 盒模型中的屬性,尚未得到瀏覽器的廣泛支援。他們在之前IE版本中同樣沒有觸發 layout 的功能)

這麼多的觸發條件,其實需要記住的就是:最無害和被大家用來處理舊版本IE閉合浮動相容性的是zoom

BFC

對於為什麼觸發了BFC可以實現閉合浮動的效果,從BFC的定義來看:

A block formatting context contains everything inside of the element creating it that is not also inside a descendant element that creates a new block formatting context.

簡單來說,BFC的一個最重要的效果是,讓處於BFC內部的元素與外部的元素相互隔離,使內外元素的定位不會相互影響,這是利用BFC清除浮動所利用的特性。

此外BFC的特性主要的可以記住以下幾點:

  • 內部的盒會在垂直方向一個接一個排列,也就是BFC內部是一個普通流;
  • 處於同一個BFC中的元素相互影響,可能會發生margin collapse;【防止上下margin collapse問題
  • 每個元素的margin box的左邊,與容器塊border box的左邊相接觸(對於從左往右的格式化,否則相反)。即使存在浮動也是如此; BFC就是頁面上的一個隔離的獨立容器,容器裡面的子元素不會影響到外面的元素,反之亦然;
  • 計算BFC的高度時,考慮BFC所包含的所有元素,連浮動元素也參與計算;【解決“高度塌陷問題”
  • 浮動盒區域不疊加到BFC上;【文字清除浮動,防止文字環繞效果

清除浮動

清除浮動用的就是clear屬性配合both屬性值了。可以用額外的元素放到包含塊的尾部,或者使用偽元素設定clear both來實現。個人推薦比較好的方法是使用偽類,然後配合IE的hasLayout相容的方法,在一絲姐姐的文章中的那個最佳解:

.clearfix:after {
    content:".";
    display:block;
    height:0;
    visibility:hidden;
    clear:both;
}
.clearfix {
    *zoom:1;
}
複製程式碼

另外在一絲的文章的最後提到了Nicolas Gallagher的文章中的方法:

.clearfix:before,.clearfix:after {
    content:"";
    display:table;
}
.clearfix:after {
    clear:both;
}/* For IE 6/7 (trigger hasLayout) */
.clearfix {
    zoom:1;
}
複製程式碼

這裡的重點是這個before的偽元素的作用,效果是處理內部元素的margin和上面的外部的元素的margin發生疊加的。我的理解是清除浮動的作用只是在元素的最後通過clear: both的方法實現了清除浮動從而消除了高度塌陷這些問題。但是本質上並沒有形成一個BFC,也就是不會有BFC的其他的一些作用,比如防止上下margin collapse的問題。這裡通過設定一個before的的display的table的元素(形成BFC)來解決頂部margin collapse的問題。

參考資料:

相關文章