DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> CSS入門知識 >> CSS特效代碼 >> CSS魔法堂:重新認識Box Model、IFC、BFC和Collapsing margins
CSS魔法堂:重新認識Box Model、IFC、BFC和Collapsing margins
編輯:CSS特效代碼     

前言                                
  盒子模型作為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來理解了!)
因此大家請注意,前方高能,前方高能!!!

 和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或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的盒子與in-flow或floated的兄弟盒子均不會發生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;}


總結                              
  若有纰漏,望各位指正,謝謝!
何問起

XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved