用 :after 清除浮動,:before 處理 Margin Collpase 怎麼理解?

BFC 與 hasLayout 都看過,但理解不深,希望可以舉個例子說明下清除浮動時,加上:before 處理 margin collpase 的原理。


準確地說,並不是清除浮動,而是清除浮動後造成的影響,元素設置為float:left或者float:right之後,會脫離文檔流,簡單來講,就是該元素的位置不屬於該元素了。所以會造成浮動元素之後的元素替代佔有該元素的位置。同樣,也會造成父級元素高度不能自適應為子元素的高度。

所以通常來講,清除浮動的方式有以下幾種,

  1. 設置父級元素overflow:hidden 觸發BFC使父級元素自適應為子元素的高度。

  2. 在浮動元素之後增加一個沒有實際作用的元素代替實際的元素替代浮動元素之後的元素受到浮動的影響,簡單來講,就是李代桃僵,借屍還魂。
  3. clearfix方式

第三種實際是第二種的升級版,代碼說明:

未清除浮動:

&
&
&
&
&clear float&
& *{margin: 0;padding: 0;list-style-type: none;}
.fl{float: left;}
.parent{width: 300px;background-color:#00f;}
.children{width:100px;height:100px;}
.green{background-color:#0f0;}
.red{background-color:#f00;}
.others{width:240px;height:40px;background-color:#ccc;}
&
&

&
&
&
子級元素1
& &子級元素2& & &其他元素& &

&

結果如下:

很明顯:父級元素未顯示為藍色,因為沒有設置高度,也沒有自適應為內容的高度,子元素1和子元素2浮動造成了自身脫離了文檔流,其他元素受到影響,佔據了浮動元素的位置。overflow:hidden方式觸發BFC

&
&
&
&
&clear float&
& *{margin: 0;padding: 0;list-style-type: none;}
.fl{float: left;}
.parent{width: 300px;background-color:#00f;overflow:hidden;}
.children{width:100px;height:100px;}
.green{background-color:#0f0;}
.red{background-color:#f00;}
.others{width:240px;height:40px;background-color:#ccc;}
&
&

&
&
&
子級元素1
& &子級元素2& & &其他元素& &

&

設置父元素overflow:hidden 觸發了BFC,父級高度自適應為內容高度,其他元素沒有受到浮動的影響。

增加空元素清除浮動方式

&
&
&
&
&clear float&
& *{margin: 0;padding: 0;list-style-type: none;}
.fl{float: left;}
.parent{width: 300px;background-color:#00f;}
.children{width:100px;height:100px;}
.green{background-color:#0f0;}
.red{background-color:#f00;}
.others{width:240px;height:40px;background-color:#ccc;}
.clear{clear:both;}
&
&

&
&
&
子級元素1
& &子級元素2& && & &其他元素& &

&

相比最初的代碼,新增了一個空元素&&

給.clear設置了clear:both,代替其他元素受到浮動的影響,也就是李代桃僵。

效果如下:

:after 方式為空元素的升級版

&
&
&
&
&clear float&
& *{margin: 0;padding: 0;list-style-type: none;}
.fl{float: left;}
.parent{width: 300px;background-color:#00f;}
.children{width:100px;height:100px;}
.green{background-color:#0f0;}
.red{background-color:#f00;}
.others{width:240px;height:40px;background-color:#ccc;}
.clearfix:after {
content: "";
display: block;
height: 0;
visibility: hidden;
clear: both;
}
.clearfix {
*zoom: 1;
}
&
&

&
&
&
子級元素1
& &子級元素2& & &其他元素& &

&

效果如下:

和第二種方式一樣,也是利用空元素替代接受浮動的影響,只不過是利用偽類:after 生成空元素

clearfix:after生成了一個『 』空元素,該元素寬高均為0,等同於第二種方式中的"&&",生成的元素在子級元素2之後。然後該元素同樣有屬性clear:both,同樣的原理,清除了浮動的影響。*zoom: 1;是在IE6 7下觸發hasLayout布局,清除浮動造成的影響。

同樣,由於清除垂直外邊距合併也有用空元素的方式,讓空元素代替和上面的元素髮生合併,由於空元素margin-top為0,所以清除了合併的影響,也同樣如此,利用偽類:before也可以在元素內部第一個位置生成空元素,清除掉collapse造成的影響。


Margin collapsing occurs in three basic cases:

Adjacent siblings

The margins of adjacent siblings are collapsed (except when the later sibling needs to be cleared past floats). For example:

&

The bottom margin of this paragraph is collapsed...& &

...with the top margin of this paragraph.&

Parent and first/last child

If there is no border, padding, inline content, or clearance to separate the margin-top of a block with the margin-top of its first child block, or no border, padding, inline content, height, min-height, or max-height to separate the margin-bottom of a block with the margin-bottom of its last child, then those margins collapse. The collapsed margin ends up outside the parent.

Empty blocks

If there is no border, padding, inline content, height, or min-height to separate a block"s margin-top from its margin-bottom, then its top and bottom margins collapse.

Margins of floating and absolutely positioned elements never collapse.

引自MDN, 描述了會發生margin collaspe的三種基本情況。注意第二種情況,其實不太嚴謹,w3上的說法是第一個first in-flow child,而不是單純的first child。如果父元素的margin-top和子元素的margin-top沒有border,padding之類的分隔,子元素和父元素的margin-top發生摺疊,注意第二條結尾,摺疊的外邊距會跑到父元素外面。此外,浮動元素不會發生外邊距摺疊。

拿你給的鏈接上例子來說

& *{ color: #fff; text-align: center;}

.demoA {
width: 300px;
background-color: #DDE3E3
}

.demoB {
background-color: #24506C;
opacity: 0.8;
float:left;
width: 80px;
height: 80px;
}

.demoC {
width: 160px;
height: 40px;
background-color: #CAD8E2;
margin-top: 40px;
}

.demoD {
background-color: #1C2C4E;
width: 160px;
height: 40px;
}

.demoE {
height: 40px;
width: 300px;
background-color: #ccc;
margin-bottom:20px;
}

/*.clearfix:before,*/
.clearfix:after {content: ""; display: table;}
.clearfix:after {clear: both; overflow: hidden;}
.clearfix {zoom:1;}
&

&Sibling& &
&demoB-float& &demoC& & &Sibling&

結果如圖,demoC是第一個in-flow的child,demoC和父元素demoA的margin-top摺疊,同時摺疊的外間距跑到了父元素外面。再結合MDN的第一條,相鄰塊狀元素的margin會摺疊,所以demoA+demoC的margin-top和demoE(第一個sibling) 的margin-bottom摺疊了,最後的margin等於最大的那個margin, 這裡就是demoC的margin-top。

根據MDN第二條,demoA和demoC的margin-top沒被分隔,因此出現了上面的情況。例如,我們給demoA加個padding-top來分割,

.demoA {
width: 300px;
padding-top: 1px;
background-color: #DDE3E3
}

結果如下

demoC的margin-top回到了父元素內。所以clearfix的原理類似,clearfix:before內的display:table創建了BFC,從而分隔了父元素demoA和子元素demoC的margin-top,於是就消除了margin collapse。

最後,如果你明白的話就知道你鏈接里的那個解釋是錯的,首先,float創建了BFC,但是和例子中的margin collapse沒關係,w3中有提到

Two margins are adjoining if and only if:

  • both belong to in-flow block-level boxes that participate in the same block formatting context

在同一BFC內的in-flow塊狀元素間才會發生margin collapse,float元素是不會發生margin collapse的。此外,例子中clearfix是給demoA的,所以clearfix:before清除的margin collapse不是demoE和demoC的,而是demoC和demoA的。如果你給demoA也加上margin-top的話,demoE和demoA依然會發生margin collapse。

更多詳情,移步

Mastering margin collapsing

Visual formatting model

Box model


推薦閱讀:

關於「真阿當」對目前流行前端技術的批判,大家有什麼看法?
在CSS中所謂「標準的盒模型」有幾種,IE早期的盒模型是標準盒模型嗎?
前端,準備年後跳槽,從現在開始準備,該制定怎樣的計劃?
為什麼很多瀏覽器不直接支持原始的 CSS3 樣式,而要使用特定前綴(如 -webkit/-moz 等)呢?
寫前端頁面的時候,是先把html骨架寫好再寫css,還是一邊寫html,一邊寫css?

TAG:前端開發 | CSS | CSS3 |