1. 程式人生 > >盒子模型、IFC、BFC和Collapsing margins

盒子模型、IFC、BFC和Collapsing margins

針對 page bfc 目標 排列 結構 then lba def

前言                                
盒子模型作為CSS基礎中的基礎,曾一度以為掌握了IE和W3C標準下的塊級盒子模型即可,但近日在學習行級盒子模型時發現原來當初是如此幼稚可笑。本文嘗試全面敘述塊級、行級盒子模型的特性。作為近日學習的記錄。

何為盒子模型?                           
盒子模型到底何方神聖居然可以作為CSS的基礎?聞名不如見面,上圖了餵!
技術分享
再來張切面圖吧!
技術分享
下面我們以 <div></div> 為栗子。<div></div> 標簽被瀏覽器解析後會生成div元素並添加到document tree中,但CSS作用的對象並不是document tree,而是根據document tree生成的render tree,而盒子模型就是render tree的節點。

* 註意:
* 1. CSS作用的是盒子(Box), 而不是元素(Element);
* 2. JS無法直接操作盒子。

盒子模型的結構
由於塊級盒子在驗證效果時幹擾信息更少,便於理解盒子模型,因此下面將以塊級盒子模型來講解。
註意: 行級盒子模型與塊級盒子模型結構一致,只是行級盒子在此基礎上有自身特性而已。
從上面兩幅圖說明盒子模型其實就是由以下4個盒子組成:
1. content box:必備,由content area和4條content/inner edge組成;
2. padding box:可選,由padding和4條padding edge組成。若padding寬度設置為0,則padding edge與content edage重疊;
3. border box:可選,由border和4條border edge組成。若border寬度設置為0,則border edge與padding edage重疊;
4. margin box:可選,由margin和4條margin/outer edge組成。若margin寬度設置為0,則margin edge與border edage重疊。
對於剛接觸CSS的同學,經常會將"通過width/height屬性設置div元素的寬/高"掛在口邊,其實這句話是有誤的。
1. 首先css屬性width和height作用於div元素所產生的盒子,而不是元素本身;
2. 另外盒子模型由4個盒子組成,那width和height到底是作用於哪些盒子呢?
這裏就分為IE盒子模型和標準盒子模型了。
IE box model
IE5.5(怪異模式)采用IE盒子模型,其它將使用W3C標準盒子模型。
技術分享

width = content-width + padding-width + border-width
height = content-height + padding-height + border-height


Standard box model
技術分享

width = content-width
height = content-height


遊走於IE box model 和 Standard box model間的通道——box-sizing屬性
我們看到存在兩種width/height的劃分方式,到底哪種才對呢?其實兩種都對,具體看如何使用而已。另外IE8開始支持CSS3屬性box-sizing,讓我們可以自由選擇采用哪種盒子:)
box-sizing:content-box/border-box/inherit
content-box——默認值,采用Standard box model
border-box——采用IE box model
inherit——繼承父元素屬性值
sample:

技術分享
Element{
  -moz-box-sizing: border-box; // FireFox3.5+
  -o-box-sizing: border-box; // Opera9.6(Presto內核)
  -webkit-box-sizing: border-box; // Safari3.2+
  -ms-box-sizing: border-box; // IE8
  box-sizing: border-box; // IE9+,Chrome10.0+,Safari5.1+,Opera10.6
}
技術分享


行級盒子——懷疑人生de起點:)                  
之前我理解的盒子模型如上所述,當我看到行級盒子的種種現象時,便開始懷疑人生了:(
width/height不起作用。。。

技術分享
.defined-wh{
  width: 100px;
  height: 50px;

  border: solid 1px red;
  background: yellow;
}
技術分享

對於block-level box

<div class="defined-wh"></div>

技術分享
對於inline-level box

<span class="defined-wh"></span>

技術分享
行級盒子的寬度怎麽會是0呢?高度是有的但不是50px啊,到底什麽回事啊?
原因很簡單,那就是行級盒子的content box的高/寬根本就不是通過height/width來設置的。
content box/area的高由font-size決定的;
content box/area的寬等於其子行級盒子的外寬度(margin+border+padding+content width)之和。

行級盒子被擠斷了。。。

.broken{
  border: solid 1px red;
  background: yellow;
}

對於block-level box

<div class="broken">一段文字一段文字一段文字一段文字一段文字一段文字</div>

技術分享
對於inline-level box

<span class="broken">一段文字一段文字一段文字一段文字一段文字一段文字</span>

技術分享
行級盒子被五馬分屍了,可憐兮兮的。更可憐的是我理解不了。。。
其實W3C Recommendation有說明的哦!
>The box model for inline elements in bidirectional context
>When the element‘s ‘direction‘ property is ‘ltr‘, the left-most generated box of the first line box in which the element appears has the left margin, left border and left padding, and the right-most generated box of the last line box in which the element appears has the right padding, right border and right margin.
>When the element‘s ‘direction‘ property is ‘rtl‘, the right-most generated box of the first line box in which the element appears has the right padding, right border and right margin, and the left-most generated box of the last line box in which the element appears has the left margin, left border and left padding.
就是說當inline-level box寬度大於父容器寬度時會被拆分成多個inline-level box,
當屬性direction為ltr時,margin/border/padding-left將作用於第一個的inline-level box,margin/border/padding-right將作用於最後一個的inline-level box;若屬性direction為rtl時,margin/border/padding-right將作用於第一個的inline-level box,margin/border/padding-left將作用於最後一個的inline-level box。
看到了沒?行級盒子真的會被分屍的,好殘忍哦:|

行級盒子怎麽不占空間了?怎麽刷存在感啊。。。

技術分享
.existed{
  margin: 20px;
  padding: 20px;
  border: solid 1px red;
  background: yellow;
  background-clip: content-box;
}
技術分享

對於block-level box

<div>before bababababababa</div>
<div class="existed">babababababababababa</div>
<div>after bababababababa</div>

技術分享
對於inline-level box

<div>before bababababababa</div>
<span class="existed">babababababababababa</span>
<div>after bababababababa</div>

技術分享
看,行級盒子的margin/border/padding-top/bottom怎麽均不占空間的?難道行級盒子僅有content box占空間嗎?
這裏已經涉及到水平和垂直方向排版的範疇了,僅以盒子模型已無法解析理解上述的問題。
(要結合https://www.w3.org/TR/CSS2/box.html和https://www.w3.org/TR/CSS21/visuren.html、https://www.w3.org/TR/CSS21/visudet.html來理解了!)

在深入解釋inline-level box的上述現象前,我們需要補充一下:
1. 一個元素會對應0~N個box;(當設置`display:none;`時,則對應0個box)
2. 根據`display`屬性值,元素會對應不同類型的controlling box(inline/block-level box均是controlling box的子類). 就CSS2而言`display:inline|inline-block|inline-table|table-cell|table-column-group`的元素對應inline-level box,而`display:block|list-item|table|table-caption|table-header-group|table-row|table-row-group|table-footer-group`的元素則對應block-level box;
3. box布局/排版時涉及到定位問題,而CSS中通過positioning scheme來定義,其包含normal flow、floats和absolute positioning三種定位方式.而normal flow包含block formatting、inline formatting和relative positioning,其中BFC為block formatting的上下文,IFC為inline formatting的上下文。

因此大家請註意,前方高能,前方高能!!!

和IFC一起看inline-level box
IFC(Inline Formatting Context),直譯為“行內格式化上下文”,這是什麽鬼的翻譯啊?反正我對於名詞一向采用拿來主義,理解名詞背後的含義才是硬道理。
我們簡單理解為每個盒子都有一個FC特性,不同的FC值代表一組盒子不同的排列方式。有的FC值表示盒子從上到下垂直排列,有的FC值表示盒子從左到右水平排列等等。而IFC則是表示盒子從左到右的水平排列方式,僅此而已(註意:一個盒子僅且僅有一個FC值)。而inline-level box的FC特性值固定為IFC
另外僅處於in-flow的盒子才具有FC特性,也就是positioning scheme必須為Normal flow的盒子才具有FC特性。
除了IFC外,對於inline-level box排版而言還有另一個重要的對象,那就是line box。line box是一個看不見摸不著的邊框,但每一行所占的垂直高度其實是指line box的高度,而不是inline-level box的高度。
line box的特點:
1. 同一行inline-level box均屬於同一個line box;
2. line box高度的計算方式(https://www.w3.org/TR/CSS21/visudet.html#line-height)
>The height of each inline-level box in the line box is calculated. For replaced elements, inline-block elements, and inline-table elements, this is the height of their margin box; for inline boxes, this is their ‘line-height‘.
>The inline-level boxes are aligned vertically according to their ‘vertical-align‘ property. In case they are aligned ‘top‘ or ‘bottom‘, they must be aligned so as to minimize the line box height. If such boxes are tall enough, there are multiple solutions and CSS 2.1 does not define the position of the line box‘s baseline.
>The line box height is the distance between the uppermost box top and the lowermost box bottom.

技術分享
.parent{
  line-height: 1;
  font-size: 14px;
 
  border: solid 1px yellow;
}
.child{
  font-size: 30px;
  vertical-align: middle;
 
  border: solid 1px blue;
}
.inline-block{
  display: inline-block;
  overflow: hidden;
 
  border: solid 1px red;
}
.other{
  border: solid 1px green;
}
技術分享 技術分享
<span class="parent">
  <span class="child">
    <span class="inline-block">display:inline-block元素</span>
    xp子元素的文字
  </span>
  xp父元素的文字
</span>
<div class="other">其他元素</div>
技術分享

技術分享
1. 根據規則,span.parent所在行的line box的高度受span.parent、span.child、span.inline-block元素對應的inline-level box"高度"的影響。其中span.parent的"高度"為其line-height實際值,span.child的"高度"為其line-height實際值,而span.inline-block的"高度"為其margin box的高度。由於設置line-height:1,因此span.parent和span.child的content box高度等於line-height實際值;
2. 根據vertical-align屬性垂直對齊,造成各“高度”間並不以上邊界或下邊界對齊;
3. span.inline-block紅色的上邊框(border top)到span.child藍色的下邊框(border bottom)的距離再減去1px即為line box的高度。(line box的下界其實是span.child的content box的下限的,你看"其他元素"的上邊框不是和span.child的下邊框重疊了嗎?如果那是line box的下界,那怎會出現重疊呢)

這裏又涉及到另一個屬性vertical-align了,由於它十分復雜,還是另開文章來敘述吧!

行級盒子小結
**就盒子模型而言**
1. inline-level box與block-level box結構一致;
2. content box的高度僅能通過屬性font-size來設置,content box的寬度則自適應其內容而無法通過屬性width設置;
3. 當inline-level box的寬度大於containing block,且達到內容換行條件時,會將inline-level拆散為多個inline-level box並分布到多行中,然後當屬性direction為ltr時,margin/border/padding-left將作用於第一個的inline-level box,margin/border/padding-right將作用於最後一個的inline-level box;若屬性direction為rtl時,margin/border/padding-right將作用於第一個的inline-level box,margin/border/padding-left將作用於最後一個的inline-level box。

**垂直排版特性**
inline-level box排版單位不是其本身,而是line box。重點在於line box高度的計算。
1. 位於該行上的所有in-flow的inline-level box均參與該行line box高度的計算;(註意:是所有inline-level box,而不僅僅是子元素所生成的inline-level box)
2. replaced elements, inline-block elements, and inline-table elements將以其對應的opaque inline-level box的margin box高度參與line box高度的計算。而其他inline-level box則以line-height的實際值參與line box高度的計算;
3. 各inline-level box根據vertical-align屬性值相對各自的父容器作垂直方向對齊;
4. 最上方的box的上邊界到最下方的下邊界則是line box的高度。(表述不夠清晰,請參考實例理解)

Collapsing margins                      
大家必定聽過或遇過collapsing margins吧,它是in-flow的block-level box排版時的一類現象。說到排版那必須引入另一個FC特性值——BFC(Block Formatting Context)的。
BFC則是表示盒子從上到下的垂直排列方式,僅此而已(註意:一個盒子僅且僅有一個FC值)。而block-level box的FC特性值固定為BFC。
collapsing margins規則
1. 元素自身margin-top/bottom collapsing

anonymous block-level box
<div class="margins"></div>
anonymous block-level box
<div class="margins border"></div>
anonymous block-level box
.margins{margin: 50px 0 70px;}
.border{border: solid 1px red;}

技術分享
當block-level box高度為0,垂直方向的border和padding為0,並且沒有in-flow的子元素。那麽它垂直方向的margin將會發生重疊。

2. 父子元素margin-top/top 或 margin-bottom/bottom collapsing

技術分享
anonymous block-level box
<div class="parent-margins">
  <div class="margins border"></div>
  anonymous block-level box
  <div class="margins border"></div>
</div>
anonymous block-level box
<div class="parent-margins border">
  <div class="margins border"></div>
  anonymous block-level box
  <div class="margins border"></div>
</div>
anonymous block-level box
技術分享
.parent-margins{margin: 25px 0;}
.margins{margin: 50px 0 25px;}
.border{border: solid 1px red;}

技術分享
當父子元素margin-top間或margin-bottom間沒有padding、border阻隔時,則會margin會發生重疊。
註意空白字符會造成目標父子元素間的存在anonymous block-level box,導致margin不重疊。

技術分享
anonymous block-level box
<div class="parent-margins">&nbsp;
  <div class="margins border"></div>
  anonymous block-level box
  <div class="margins border"></div>
</div>
anonymous block-level box
技術分享

技術分享

3. 兄弟元素margin-bottom/top collapsing

<div class="margins">former</div>
<div class="margins">latter</div>
.margins{margin: 50px 0 25px;}

兩個相鄰的in-flow block-level box的上下margin將發生重疊。

**上述為默認情況下block-level box(即display:block,其它為默認值時)的margin重疊規則**
那非默認情況下呢?相比非默認情況下的margin重疊規則,我們更關心是什麽時候不會產生重疊。這時又引入了另一個概念——生成新BFC。也就是block-level box A與block-level box B的FC特性值BFC可能是不同的。
當兩個相鄰box的FC值不為同一個BFC時,它們的margin絕對不會重疊。
那麽剩下的問題就是,到底何時會產生新的BFC?哪些block-level box會采用新的BFC?默認BFC又是誰生成的呢?
其實根元素(html)會生成默認BFC供其子孫block-level box使用。
采用floats或absolute positioning作為positioning scheme時,或display:inline-block/table-cell/table-caption/flex/inline-flex或overflow屬性值不為visible時,則會產生新的BFC;而新的BFC將作為子孫block-level box的FC屬性值。
註意:
1. 產生新BFC的盒子不會與子盒子發生margin重疊;
2. display:inline-block的盒子不與 兄弟 和 父 盒子發生margin重疊,是因為display:inline-block的盒子的FC特性值為IFC,還記得line box嗎?沒有margin重疊是自然不過的事了;
3. positioning scheme為floats的盒子不與floated的兄弟盒子發生margin重疊,也不會與前一個in-flow的兄弟盒子發生margin重疊。(註意:與父盒子也不會發生margin重疊)

<div class="margins border">sibling</div>
<div class="margins border float">floats1</div>
<div class="margins border float">floats2</div>
.margins{margin: 50px 0 50px;}
.border{border: solid 1px red;}
.float{float:left;width:200px;}

技術分享

歸納FC、BFC和IFC                      

由於上述主要闡述inline/block-level box,因此通過“僅此而已”來簡化BFC和IFC的內涵。下面我們稍微全面一點去理解BFC和IFC如何影響inline/block-level box。

FC(Formatting Context),用於初始化時設置盒子自身尺寸和排版規則。註意“初始化”,暗指positioning scheme采用的是normal flow,要知道floats和absolute positioning均不是默認/初始化值。也就是說我們在討論FC及BFC和IFC時,均針對in-flow box而言的。
BFC
**對於不產生新BFC的盒子**
1. block-level boxes垂直排列,盒子的left outer edge與所在的containing block的左邊相接觸,默認情況下(width為auto時)right outer edge則與所在的containing block的右邊相接觸。即使存在floated的兄弟盒子。

<div id="container" style="border:solid 2px red;">
  <div id="left" style="float:left;width:300px;height:30px;background:yellow;opacity:0.2;"></div>
  <div id="right" style="height:30px;background:#999;"></div>
</div>

技術分享

雖然 div#left 浮點了,但 div#right 的left outer edge還是與 div#container 的left content edge相接觸。 div#right 所在的containing block就是 div#container 的content box.
2. block-level box高度的計算
The element‘s height is the distance from its top content edge to the first applicable of the following:
the bottom edge of the last line box, if the box establishes a inline formatting context with one or more lines
the bottom edge of the bottom (possibly collapsed) margin of its last in-flow child, if the child‘s bottom margin does not collapse with the element‘s bottom margin
the bottom border edge of the last in-flow child whose top margin doesn‘t collapse with the element‘s bottom margin
zero, otherwise
Only children in the normal flow are taken into account (i.e., floating boxes and absolutely positioned boxes are ignored, and relatively positioned boxes are considered without their offset).

也就out-flow box不影響block-level box高度的計算。也就是解釋了為何div中僅含floated元素時,div盒子高度為0的現象了。

**對於產生新BFC的盒子**
對於產生新BFC的盒子而言,除了不發生collapsing margins的情況外,還有兩個與浮點相關的現象。
1. out-flow box納入block-level box高度的計算
In addition, if the element has any floating descendants whose bottom margin edge is below the element‘s bottom content edge, then the height is increased to include those edges. Only floats that participate in this block formatting context are taken into account, e.g., floats inside absolutely positioned descendants or other floats are not.
也就positioning scheme為floats的box也會影響block-level box高度的計算。

2. 誓死不與positioning scheme為floats的兄弟盒子重疊
The border box of a table, a block-level replaced element, or an element in the normal flow that establishes a new block formatting context (such as an element with ‘overflow‘ other than ‘visible‘) must not overlap the margin box of any floats in the same block formatting context as the element itself. If necessary, implementations should clear the said element by placing it below any preceding floats, but may place it adjacent to such floats if there is sufficient space. They may even make the border box of said element narrower than defined by section 10.3.3. CSS2 does not define when a UA may put said element next to the float or by how much said element may become narrower.

產生新BFC的block-level box不與floated-box重疊,而是floated-box的margin-box與block-level box的border-box相接觸。
水平方向

<div style="float:left;width:100px;border: solid 1px red;margin-right:50px;">floated</div>
<div style="width:200px;border: solid 1px blue;margin-left:100px;overflow:hidden;">gen new BFC balabala</div>

技術分享
垂直方向

<div style="float:left;width:100px;border: solid 1px red;margin-bottom:50px;">floated</div>
<div style="width:200px;border: solid 1px blue;margin-top:100px;overflow:hidden;">gen new BFC balabala</div>

技術分享

IFC

提起IFC那就不能不說line box,而line box高度的計算方式上面已經敘述了,那line box的寬度呢?
line box默認情況下左邊框與containing block的左邊框接觸,右邊框與containing block的右邊框接觸。若存在floated兄弟盒子,則line box的寬度為containing block的寬度減去floated-box的outer-box的寬度。
技術分享
而inline-level box必須包含在line box中,若inline-level box的`white-space:nowrap或pre外的其他值`時,就會將inline-level box拆分為多個inline-level box並散落到多個line box中,從而實現文字環繞圖片的效果了。
技術分享
否則inline-level box會捅破line box(即line box寬度不變)

    行——換與不    

先看看關於換行的CSS屬性吧!

技術分享
white-space
    normal: 忽略/合並空白
    pre: 保留空白,如同<pre>的行為
    nowrap: 忽略/合並空白,文本不會換行,直到遇到<br/>
    pre-wrap: 保留空白,但是會正常地進行換行
     pre-line: 忽略/合並空白,但是會正常地進行換行
    inherit: 從父元素繼承。
  word-wrap
    normal: 只在允許的斷字點換行
    break-word: 在長單詞或URL地址內部進行換行
  word-break
    normal:依照亞洲和非亞洲語言的文本規則,允許在單詞內換行。
    keep-all:讓亞洲語言文本如同非亞洲語言文本那樣不允許在任意單詞內換行。
    break-all:允許非亞洲語言文本行如同亞洲語言文本那樣可以在任意單詞內換行。
技術分享

具體示例可參考:css中強制換行word-break、word-wrap、white-space區別實例說明

在處理換行問題上,我們要處理的對象分為亞洲語言文本和非亞洲語言文本。對於亞洲語言文本是以字作為操作單元,而非亞洲語言文本是以單詞作為操作單元。而換行是針對特定語言文本的操作單元來處理,所以默認情況下會看到一串沒空格的“中文”自動換行,而一串沒空格的“英文”卻沒有換行的現象。
對於我們(亞洲人)而言,一般采用 word-break:break-all;word-wrap:break-word; 來實現中英文自動換行效果,但英文單詞本身是不能這樣簡單粗暴地換行的。
英語單詞移行有一定規則,歸納如下:
1.移行處要用連字符號“-”,只占一個印刷符號的位置並放在該行的最後.
2.移行時一般按照音節進行,故只可在兩音節之間分開,不能把一個完整的音節分寫在上下兩行.例如:Octo-ber(正),Octob-er(誤).
3.復合詞要在構成該詞的兩部分之間移行.如:some-thing,bed-room等.
4.如果復合詞原來就有連字符號,則就在原連字符號處分行.如:good-looking等.
5.兩個不同的輔音字母在一起時,移行時前後各一個.如:cap-tain,ex-pose等.
6.當兩個音節間只有一個輔音字母時,如果該輔音字母前的元音字母按重讀開音節的規則發音,該輔音字母移至下一行.如:fa-ther等.但如果元音按重讀閉音節的規則發音,則該輔音字母保留在上一行末尾.例如:man-age等.
7.當遇到雙寫輔音字母時,一般把它們分成前後各一個.例如:mat-ter等.
8.當重讀音節在後面時,元音字母前的輔音字母通常移到下一行.如:po-lite等.
9.單音節詞不可移行.如:length,long,dance等.
10.前綴或後綴要保持完整,不可分開寫.如:unfit,disappear等.
11.阿拉伯數字不分開移行書寫.
12.不論音節多少,專有名詞不宜分寫.例如:Nancy,Russia等.
13.縮寫詞、略寫詞或某些詞的縮寫形式不可移行書寫.例如:U.N.(聯合國),P.R.C.(中華人民共和國),isn‘t.
14.不能構成一個音節的詞尾不分寫.例如:stopped等.
15.字母組合或輔音連綴不可移行.例如:machine,meat等.

CSS簡化了上述的規則,若需要換行處恰好是一個復合詞,就在原連字符號處分行;其它情況則整個單詞移到下一行。因此使用 word-wrap:break-word; 就OK了。

另外我們還可以通過 word-break:keep-all;white-space:nowrap; 來實現打死都不換行的效果


總結                              
洋洋灑灑總算把Box Model、BFC和IFC描述了個大概。對於BFC折騰點就是在collapsing margins那,另外還有產生新BFC這個行為上(這個跟浮動等有交集,以後再理清吧);而IFC重點在於理解line box,其實line box也像block-level box那樣是垂直排列的,而inline-level box則是以line box作為容器實現水平排列罷了。到這裏會發現理解IFC比BFC蛋疼多了,不過有了這篇作基礎,後面理解text-align、line-height和vertical-align就輕松不少了。

本文純個人理解,若有紕漏,望各位指正,謝謝!
尊重原創,轉載請註明來自:http://www.cnblogs.com/fsjohnhuang/p/5259121.html肥子John^_^

感謝                              

http://div.io/topic/834?page=1#3261(BFC)

http://www.cnblogs.com/giggle/p/5236982.html(BFC)

https://segmentfault.com/a/1190000003043991 (IFC)

http://www.cnblogs.com/winter-cn/archive/2013/05/11/3072929.html(BFC/IFC)

[KB010: 常規流( Normal flow ) ](http://www.w3help.org/zh-cn/kb/010/)
[CSS 101: Block Formatting Contexts](http://yuiblog.com/blog/2010/05/19/css-101-block-formatting-contexts/)

轉載:http://www.cnblogs.com/fsjohnhuang/p/5259121.html

盒子模型、IFC、BFC和Collapsing margins