CSS2.1SPEC:視覺格式化模型之width屬性詳解(上),css2.1specwidth
在介紹了包含塊之後,CSS2.1標準中介紹了width屬性和height屬性,這兩個屬性在我們的頁面配置中也發揮著重要的作用。在盒模型中,width和height包圍了一個框的內容地區(content area),是否擁有顯式的定義、框的類型、框的布局方式、框與其他框之間的關係以及其他的額外資訊都會影響一個框的最終的width和height值,本文首先介紹width屬性以及視覺格式化模型中如何決定width值的相關演算法。
1 width屬性剖析
包含塊的寬度來計算的。但是如果包含塊的寬度需要根據包含的內容來決定,那麼最終的布局是未定義的,也就是標準中沒有明確規定如何計算寬度值。另外,如果盒子是絕對位置的,那麼它的百分比長度在計算時是根據包含塊的padding edge的寬度來計算的。註:經過實驗發現,如果一個元素的寬度為百分比值,而其包含塊的寬度又需要根據包含塊的內容來決定時,不同的情況以及不同的瀏覽器都會有完全不相同的結果,因此建議在布局時不要在這種情況下使用百分比定義寬度值。具體規律還需要再進行總結,有研究過的朋友可以分享一下。(3)auto:根據CSS標準中規定的寬度計算方法來計算,具體的演算法下文中將詳細介紹(4)inherit:事實上width屬性是不可繼承的,很奇怪為什麼會有inherit這個選項,實際情況中width屬性一般也很少用到inherit值其中,預設取值為auto1.2 適用元素width適用於除了 非置換的行內元素(預設情況下的span,em等元素),表格行(tr),表格行組(tbody)元素之外的所有其他元素。比如說,我們為span設定一個width,這個width是不會產生任何效果的,除非把span的display屬性修改為inline-block或者block。另外如1.1中提到的,width屬性是不能繼承的。
2 width,left,right,margin-left,margin-right值的計算問題
2.1 關於幾個屬性的預設值在css盒模型中,影響一個框在頁面中所佔空間大小的屬性包括width、height、margin、border、padding,另外left,right,top和bottom值也會在某些條件(position為非static)下影響框在頁面中的位置。如果我們為這幾個屬性定義了明確的值(數值或者百分比),那麼最終的使用值(used value)就是這個明確的值(對於height和width來說,可能還會受max或者min值的影響)。這篇文章只討論影響元素框在水平方向上的尺寸和位置的屬性(width、margin-left、margin-right、padding-left、padding-right、border-left-width、border-right-width、left、right)
margin-left、margin-right預設情況下的值也是0,但是它們有auto值。
width、left、right的預設取值為auto,這些屬性在預設情況下,就需要css為auto值計算出最終的使用值,而如何計算預設情況下的auto值就是下文將要重點闡述的內容。
2.2幾個尺寸和位置屬性值的計算方法首先看一下標準中的闡述:The values of an element's ’width’, ’margin-left’, ’margin-right’, ’left’ and ’right’ properties as used for layout depend on the type of box generated and on each other. (The value used for layout is sometimes referred to as the used value) In principle, the values used are the same as the computed values, with ’auto’ replaced by some suitable value, and percentages calculated based on the containing block, but there are exceptions. 也就是說元素所使用的width,margin-left,margin-right,left,right屬性會依賴於它們所產生的框的類型以及彼此之間的關係,總的來說,最終的使用值與計算值是相同的:即auto被替換成合適的值,百分比依據包含塊進行計算。但是也存在例外,而例外的情況才是重點所在。
Note. The used value of ’width’ calculated below is a tentative value, and may have to be calculated multiple times, depending on ’min-width’ and ’max-width’, see the section Minimum and maximum widths below. 也就是說,通過計算出的width值是一個不確定的值,因為width可能還會受到min-width或者max-width值的影響,本文最後也會對此進行介紹。
註:為了顯示效果,本文所有的demo中都為body賦予了10px的margin,10px的padding,以及3px的border2.2.1 非置換的行內元素對於非置換的行內元素,margin的auto值將被賦予0,而width屬性是不適用的。對於非置換的行內元素來說,它的實際寬度要取決於其包含的實際內容(一般都為文本),同時還會受到word-break,word-wrap以及white-space屬性的影響。這裡列舉一個例子: ##DEMO 1 行內非置換元素的寬度問題 有如下的一個span: <body><span> ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd</span></body>
我們知道,英文字元來說,如果沒有出現空格或者標點符號,那麼是不允許換行的,所以我們看到span的寬度為:
可以看到,span的寬度為1736px,在IE6+中效果相同。如果為span增加一個css屬性,即:word-wrap: break-word,我們知道,增加這個屬性之後就允許英文單詞的強制換行了,效果如下:
由於行內框的寬度是由包含塊給的,因此到了body的content area的邊界時也就到了行框的邊界,因為我們增加了word-wrap:break-word之後,允許強制換行,因此當到了行框邊緣時就發生換行,而行內元素span的寬度也與行框的寬度相同了。
2.2.2 行內建換元素置換元素的內容並不受CSS渲染模型的控制,並且往往有著固有的尺寸。計算行內建換元素所產生的框的width時,則要把置換元素固有的尺寸考慮在內。首先,margin的auto值將被賦予0
IF: height和width都是auto,但是元素有著固有的寬度,那麼固有寬度就是width使用值,比如文本輸入框(input[type=text]),如果它的width和height都為預設的auto值,那麼最終的寬度就是其固有的width,但是在不同瀏覽器中這個寬度值可能存在差異。[1]=》ELSE IF:width為auto值,但元素有固有的寬高比ratio,並且height有確切的值(或者有固有的高度),則width的使用值為:(used height) * (ratio)[2]=》ELSE IF:width和height都為auto,元素有固有的寬高比ratio,但是卻沒有固有的寬度或者高度,那麼元素的寬度是未定義的。(在這種情況下,CSS2.1標準推薦在包含塊的寬度不依賴於內容的情況下,按照計算正常流中的塊級非置換元素的width值的方法來計算,見本章第3部分)[3]=》ELSE IF:元素的width屬性為auto,但是有固有的寬度值,那麼固有的寬度值就是元素width的使用值[4]=》ELSE IF:元素的width屬性為auto,但是上述的幾種情況都不符合,那麼寬度的使用值為300px,如果300px超出了使用者代理程式的寬度,那麼使用者代理程式應該用能夠容納的最大矩形框的寬度作為width的使用值,並且這個矩形的寬高比為2:1。典型的例子比如canvas和iframe。[5]##DEMO 2 iframe的width
有如下代碼:<body>
<iframe src="../fixed.html"></iframe>
</body>
顯示的效果為:
可以看到,顯示的寬度為300px,高度為150px。
2.2.3 正常流中的塊級非置換元素這種情況是我們在布局時經常會遇到的,我們在body中添加了一個div時,它的寬度是多少?為什麼兩個正常流的div不能在同一行中顯示?學習了這一節的內容,就能解答這兩個問題了。
'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 'margin-right' = width of containing block
如果width不是auto,而且'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' (再加上所有非auto的margin)的值大於包含塊的寬度,那麼margin-left和margin-right的所有的auto值在下面的計算中都將被視為0.
IF: 如果上面的值都不是auto,那麼這種情況被稱為“過約束”,在這種情況下,有一個屬性的值將會不同。如果包含塊的direction屬性為ltr,那麼就忽略margin-right,並且利用其它值以及上面的等式計算margin-right的使用值。如果是rtl,那麼就重新計算margin-left的值。[1]
##DEMO 3 過約束條件下的margin值重新計算
我們有如下代碼:<body><div style="width:100px;height: 30px;border: 1px solid #f00;" id="div"></div></body>在這種情況下,元素的顯示效果如下:
我們再修改一下代碼,為元素顯式加一個margin-right:<body><div style="width:100px;height: 30px;border: 1px solid #f00;margin-right:200px" id="div"></div></body>
<body style="direction: rtl"><div style="width:100px;height: 30px;border: 1px solid #f00;" id="div"></div></body>
顯式效果也符合標準中所述,IE6+在這一點上都是符合標準的。
=》ELSE IF:上面這些值中如果只有一個auto值,那麼就按照公式計算得出這個auto值[2]
##DEMO 4 只有一個auto值的情況
我們有如下代碼: <body>
<div style="width: 100px;height: 30px;border: 1px solid #f00;margin-left: auto;" id="div"></div>
</body>
這種情況下,只有margin-left屬性是auto值(預設為auto),我們看一下顯示的效果:
元素的margin-left值就等於包含塊的寬度- border-left-width - border-right-width - width
=》ELSE IF:width是auto,那麼其他所有的auto值都設為0,width按照等式來計算。[3]=》ELSE IF:margin-left和margin-right都是auto,那麼它們的使用值將會相同,並且最終的結果會使得該元素產生的block置中。實際上,這就是我們常用的margin auto置中的方案。[4]我們用一個DEMO來展示這個效果:##DEMO 5 width為auto以及margin-left,margin-right都為auto的情況
我們有如下代碼:<div style="width:auto;margin-left: auto;margin-right:auto;height: 30px;border: 1px solid #f00;" id="div"></div>
顯示效果為:
驗證了規則3,即如果width為auto,那麼就把其他所有的auto設為0,並且用等式計算width值。
2.2.4 正常流中的塊級置換元素
對於正常流中的塊級置換元素,在計算width值時仍然遵循2.2.2節中的規則;得到置換元素的width值之後,再按照2.2.3中的規則計算margin值。我們用一個執行個體來驗證一下:##DEMO 6 正常流中的塊級置換元素的width和margin我們有如下代碼:<body><input type="text" style="margin: auto"/></body>效果為:
因為input預設並不是一個區塊層級元素,而是一個行內級元素,所以不符合2.2.3節中的規則4,我們修改一下代碼,為input元素添加一個display:block的樣式,使其成為一個區塊層級元素:<body><input type="text" style="margin: auto;display: block"/></body>效果為:
可以看到,input元素置中顯示了。