透徹理解塊級元素的寬度

李鬆峰發表於2012-01-17

作者按:又翻出來一篇5年前(2006年12月28日)寫的關於盒模型的文章,都不知道參考了哪本書了。只能憑印象感謝 Eric Meyer 的《CSS權威指南(第2版)》(The Definitive Guide, 2nd Edition)——因為當時作為練習剛翻譯完這本書。

內容提要 透徹掌握CSS視覺化模型的原理,可以讓我們準確判斷某個意外行為到底是因為CSS樣式問題,還是CSS解析引擎問題。本文要討論的要點是,當margin-leftwidthmargin-right這三個屬性分別取auto值或大於0的值,進而形成不同組合的情況下,如何確定塊級元素中各個組成部分的寬度。

塊級元素和行內元素

CSS把處在正常文件流中的不同html元素區分為塊級元素(block element)和行內元素(inline element)。一般來說,所謂塊級元素就是指當它們顯示在瀏覽器中時,會在自身前後各插入一個空行,而使自身在頁面中佔據一個相對獨立的塊狀區域的元素。因此,HTML文件中連續的塊級元素的典型顯示方式就是“堆疊”。塊級元素的例子有h1h6divp等。而行內元素則是指以自身所包含的內容決定在頁面中佔據空間的大小,並且可以與其他行內元素共處一行的元素。行內元素的典型顯示方式就是“連線”。行內元素的例子有aimgspaninput等。對於行內元素,又可以進一步分為可替換行內元素和不可替換行內元素,兩者的區別在於元素中所包含的內容是否直接顯示在頁面中。不可替換的行內元素,如a元素中的連結文字會直接顯示在網頁中;而可替換的行內元素,如img,當它在網頁中顯示時會被其src屬性指向的影像替代。本文討論的是塊級元素,有關行內元素的更多內容請參考其他資料。

正常文件流 指的是HTML文件元素在進行內容佈局時所遵循的從左到右、從上到下的表現方式。這種文件流佈局的方式對於某些語言可能會有所不同(比如從右往左閱讀的語言)。HTML中的大多數元素都處在正常的文件流中,而一個元素要脫離正常流的唯一途徑就是浮動或定位。

塊級元素的寬度

塊級元素的寬度是指塊級元素內容區、左右內邊距、左右邊框和左右外邊距的寬度之和。而塊級元素內容區的寬度由該元素的width屬性來表示(或設定)。

enter image description here

圖1簡單的盒模型示意圖

有一個簡單的規則,就是塊級元素的寬度總是等於其父元素內容區的寬度。如果一個div元素的父元素是body,那麼這個div元素的寬度就等於body的寬度,也就是瀏覽器中視窗的寬度。一個處在正常文件流中的塊級元素,其寬度是由七個部分共同決定的。可以稱為塊級元素的水平“七屬性”,它們分別是:

  • margin-left
  • border-left
  • padding-left
  • width
  • padding-right
  • border-right
  • margin-right

enter image description here

圖2 塊級元素的水平“七屬性”

如圖2所示,塊級元素在水平方向上的寬度是由七個CSS屬性共同決定的。例如,一個div元素中包含一個p元素。而div元素的寬度是500px,那麼這個p元素內容區的寬度(width)加上其左右內邊距、左右邊框和左右外邊距之和就等於500px。這個計算過程用公式表示,就是:

margin-left + 
border-left + 
padding-left + 
width + 
padding-right + 
border-right + 
margin-right 
= 500px

自動水平屬性如何決定塊級元素的寬度

在塊級元素的水平七屬性中,只有三個屬性可以使用auto(自動)值,如圖3所示。

enter image description here

圖3 可以取auto值的水平屬性

也就是說,只有margin-leftwidthmargin-right這三個屬性可以使用auto值。這正是本文要討論的核心問題,即在上述三個屬性分別取不同的auto值和長度值,形成不同組合的情況下,相應塊級元素盒子中各部分的寬度是如何確定的?

在掌握了各種組合的可能性和相應結果的基礎上,我們就可以在遇到意外的情況時,對到底是CSS樣式的問題,還是CSS解析引擎的問題作出正確的判斷。並根據判斷來決定是修改CSS樣式、向瀏覽器提供商報告bug,還是採取過濾或hack手段來解決問題。

現在舉例子說明,假設有如下簡單的html程式碼:

<div>
    <p> </p>
</div>

而相應有8種可能的CSS規則:

1. p { margin-left:auto;width:auto;margin-right:auto; }
2. p { margin-left:50px;width:auto;margin-right:auto; }
3. p { margin-left:auto;width:auto;margin-right:50px; }
4. p { margin-left:50px;width:auto;margin-right:50px; }
5. p { margin-left:auto;width:200px;margin-right:auto; }
6. p { margin-left:50px;width:200px;margin-right:auto; }
7. p { margin-left:auto;width:200px;margin-right:50px; }
8. p { margin-left:50px;width:200px;margin-right:50px; }

將這8種可能的CSS規則應用到上面的標記以後,會得到如圖3所示的結果(圖中為父元素div新增了邊框,而為了表示內容區的寬度,也為p元素新增了背景)。

enter image description here

圖4 自動寬度的8種可能性

下面,我們就來逐一分析這8種情況:

第一種情況:

規則是 p { margin-left:auto;width:auto;margin-right:auto; }

即,三個屬性全都取auto值。如圖3所示,結果是p元素的內容區的寬度和父元素div的寬度相等。根據前面的公式(此例未考慮左右邊框和內邊距,假設它們全取預設值0)我們知道,此時的margin-left:automargin-right:auto等同於margin-left:0margin-right:0。或者說此時的左右外邊距都等於0。

第二種情況:

規則是 p { margin-left:50px;width:auto;margin-right:auto; }

即,把左外邊距明確設定為50畫素,widthmargin-right的值仍然是auto。如圖3所示,結果是p元素的內容區寬度等於div元素的寬度減去50畫素。也就是說,此時左外邊距是50畫素,而margin-right:auto相當於margin-right:0,即右外邊距為0。

第三種情況:

規則是 p { margin-left:auto;width:auto;margin-right:50px; }

與第二種情況類似,只不過是把右外邊距而不是左外邊距明確設定為50畫素。圖3所示的結果告訴我們,此時右外邊距是50畫素,而左外邊距為0。 (以上三種情況,值為auto的外邊距都被重置為預設值0)

第四種情況:

規則是 p { margin-left:50px;width:auto;margin-right:50px; }

這次是把左、右外邊距都明確地設定為50畫素,而只有width屬性是auto。如圖3所示,結果是p元素內容區的寬度等於div的寬度減去(50+50=)100畫素。也就是說,50畫素的左、右外邊距是有效的。p元素內容區在左右外邊距之間以自動適應的寬度值補足了div元素的寬度。

第五種情況:

規則是 p { margin-left:auto;width:200px;margin-right:auto; }

這次把左、右外邊距都設定為auto,而把width明確地設定為200畫素。如圖3所示,結果是p元素的內容區寬度就是設定的200畫素,而且,由於左右外邊距同為auto,使得p元素在div元素中水平居中。這種情況也是網頁佈局中最常用的居中塊級元素居的主要手段。

第六種情況:

規則是 p { margin-left:50px;width:200px;margin-right:auto; }

這次margin-leftwidth分別明確設定成了50px和200px,只有右外邊距的值是auto。從圖3中可以看到,結果是p元素的內容區就是設定的200畫素,而左外邊距也是設定的50畫素。但右外邊距此時在前兩個部分之後以自動適應的寬度值補足了div元素的寬度。

第七種情況:

規則是 p { margin-left:auto;width:200px;margin-right:50px; }

與第六種情況相似,但這次是左外邊距在後兩個部分之前以自動適應的寬度值補足了div元素的寬度。

第八種情況:

規則是 p { margin-left:50px;width:200px;margin-right:50px; }

這是一種典型的情況,即三個屬性都明確地設定相應的值。從圖3的結果中我們看到,只有左外邊距和p元素內容區的寬度是設定的值。右外邊距雖然也明確設定了50畫素的值,但實際情況就像是使用了auto的第六種情況一樣。實際上,在三個屬性都明確設定了值,但其中一個值在沒有解的情況下--即在不能滿足三者之和等於div元素寬度的情況下--在從左往右閱讀的語言中,會把右外邊距重置為自動適應的寬度值,也就是auto

小結

通過對以上8種可能情況的逐一分析,我們可以得出如下結論:

  1. width屬性的值設定成auto的情況下,塊級元素內容區的寬度取決於左右外邊距是否明確設定了值。如果左右外邊距值都是auto,則左右外邊距的值都會被重置為預設的值0;如果左右外邊距中只有一個值是auto,則該值被重置為0,另一個值有效;如果左右外邊距都設定了明確的值,兩個值都將有效,此時元素內容區的寬度就是父元素的寬度減去左右外邊距後的值。需要說明的是,左右外邊距的預設值是0,這意味著如果沒有在CSS規則中宣告margin-left或者margin-right,它們就會使用預設值0。
  2. width屬性的值設定為大於0的值的情況下,塊級元素內容區的寬度就是由width屬性設定的值。此時,左右外邊距的值如果都是auto,則會使塊級元素在其父元素中居中;如果左右外邊距中只有一個值是auto,則明確設定的值有效,auto值會自動適應剩餘的寬度;如果左右外邊距都設定了明確的值,那麼在從左往右閱讀的語言中,會把右外邊距的值重置為auto

(注:本文所說“設定明確的值”,是指設定了大於0的值)

以下是今天(2012年1月17日)在Chrome中做的一個測試的截圖。 enter image description here

相關文章