譯者注:一篇很好的文章,很久以前在blog上就推薦過,這兩天斷斷續(xù)續(xù)花了點(diǎn)時間翻譯了一下,推薦讀讀。英文原文在此。
文中所有的 layout 這個單詞都未作翻譯,一來本身這個單詞意思就比較多,翻成啥都覺得別扭,二來它也是專有的屬性,所以就意會一下吧。水平有限,很多地方都是模模糊糊地意譯,發(fā)現(xiàn)錯誤歡迎留言指出。
引用一段來自Dean Edwards的評價:
I recommend that every CSS designer and DOM scripter read this. Understanding “l(fā)ayout” gives a huge insight into lots of other IE bugs and idiosyncrasies.
(Dean Edwards)
4月16日修訂的內(nèi)容:
- 將quirks模式這一部分單獨(dú)移動到一篇文章中講述。
- 添加:邊緣裁切。
- 添加:收縮包圍(shrink-wrapping)現(xiàn)象。
5月17日修訂的內(nèi)容:
- 重寫了了浮動元素旁邊的元素這一部分。
- 部分章節(jié)小的修正:屬性,有關(guān)內(nèi)聯(lián)級別元素,CSS hacks。
- 重新整理了部分章節(jié)的語言:定義,數(shù)據(jù),問題種種,分析,清除浮動和自動擴(kuò)展適應(yīng)高度,絕對定位元素。
On having layout
- 本文修訂中
- 當(dāng)前版本:Rev. 7 2006–05–17
- 修訂歷史
- 各種語言版本
- 目錄
介紹
Internet Explorer 中有很多奇怪的渲染問題可以通過賦予其“l(fā)ayout”得到解決。John Gallant 和 Holly Bergevin 把這些問題歸類為“尺寸bug(dimensional bugs)”,意思是這些 bug 可以通過賦予相應(yīng)元素某個寬度或高度解決。這便引出關(guān)于“l(fā)ayout”的一個問題:為什么它會改變元素的渲染特性,為什么它會影響到元素之間的關(guān)系?這個問題問得很好,但卻很難回答。在這篇文章中,我們專注于這個復(fù)雜問題會有那些方面的表現(xiàn),某一方面的具體討論和范例請參考文中給出的相關(guān)鏈接。
hasLayout — 定義
“Layout”是一個 IE/Win 的私有概念,它決定了一個元素如何顯示以及約束其包含的內(nèi)容、如何與其他元素交互和建立聯(lián)系、如何響應(yīng)和傳遞應(yīng)用程序事件/用戶事件等。
這種渲染特性可以通過某些 CSS 屬性被不可逆轉(zhuǎn)地觸發(fā)。而有些 HTML 元素則默認(rèn)就具有“l(fā)ayout”。
微軟的開發(fā)者們認(rèn)為元素都應(yīng)該可以擁有一個“屬性(property)”(這是面向?qū)ο缶幊讨械囊粋€概念),于是他們便使用了 hasLayout
,這種渲染特性生效時也就是將 hasLayout 設(shè)成了 true
之時。
術(shù)語
當(dāng)我們說一個元素“得到 layout”,或者說一個元素“擁有 layout” 的時候,我們的意思是指它的微軟專有屬性 hasLayout
為此被設(shè)為了 true
。一個“l(fā)ayout元素”可以是一個默認(rèn)就擁有 layout 的元素或者是一個通過設(shè)置某些 CSS 屬性得到 layout 的元素。
而“無layout元素”,是指 hasLayout
未被觸發(fā)的元素,比如一個未設(shè)定寬高尺寸的干凈 div 元素就可以做為一個“無layout祖先”。
給一個默認(rèn)沒有 layout 的元素賦予 layout 的方法包括設(shè)置可觸發(fā) hasLayout = true
的 CSS 屬性。參考默認(rèn) layout 元素以及這些屬性列表。沒有辦法設(shè)置 hasLayout = false
, 除非把一開始那些觸發(fā) hasLayout = true
的 CSS 屬性去除。
問題種種
hasLayout
的問題不管新手還是老手,不管設(shè)計(jì)師或者程序員可能都遇到過。Layout 在顯示盒子時有著不同尋常而且難以預(yù)料的效果,而且有時甚至?xí)窟B到他們的孩子元素。
一個元素是否具有“l(fā)ayout”可能會引發(fā)如下的一些問題:
- IE 很多常見的浮動 bug 。
- 元素本身對一些基本屬性的異常處理問題。
- 容器和其子孫之間的邊距重疊(margin collapsing)問題。
- 使用列表時遇到的諸多問題。
- 背景圖像的定位偏差問題。
- 使用腳本時遇到的瀏覽器之間處理不一致的問題。
上面的列表只是列出一個大概,也不完善。下面的文章將盡可能詳細(xì)徹底的描述有無“l(fā)ayout”所帶來的各種問題。
Layout 從何而來
不同于標(biāo)準(zhǔn)屬性,也不像某些瀏覽器的私有 CSS 屬性,layout 無法通過某一個 CSS 聲明直接設(shè)定 。也就是說沒有“l(fā)ayout屬性”這么一個東西,元素要么本身自動擁有 layout,要么借助一些 CSS 聲明悄悄地獲得 layout。
默認(rèn)layout元素
下列元素應(yīng)該是默認(rèn)具有 layout 的:
html>, body>
table>, tr>, th>, td>
img>
hr>
input>, select>, textarea>, button>
iframe>, embed>, object>, applet>
marquee>
屬性
下列 CSS 屬性和取值將會讓一個元素獲得 layout:
position: absolute
- 絕對定位元素的包含區(qū)塊(containing block)就會經(jīng)常在這一方面出問題。
float: left|right
- 由于 layout 元素的特性,浮動模型會有很多怪異的表現(xiàn)。
display: inline-block
- 當(dāng)一個內(nèi)聯(lián)級別的元素需要 layout 的時候往往就要用到它,這也可能也是這個 CSS 屬性的唯一效果——讓某個元素?fù)碛?layout?!癷nline-block行為”在IE中是可以實(shí)現(xiàn)的,但是非常與眾不同: IE/Win: inline-block and hasLayout 。
width: 除 “auto” 外的任意值
- 很多人遇到 layout 相關(guān)問題發(fā)生時,一般都會先嘗試用這個來修復(fù)。
height: 除 “auto” 外的任意值
- height: 1% 就在 Holly Hack 中用到。
zoom: 除 “normal” 外的任意值
(MSDN)
- MS專有屬性,無法通過校驗(yàn)。 不過
zoom: 1
可以臨時用做調(diào)試。
writing-mode: tb-rl
(MSDN)
- MS專有屬性,無法通過校驗(yàn)。
在 IE7 中,overflow 也變成了一個 layout 觸發(fā)器:
overflow: hidden|scroll|auto
- 這個屬性在之前版本 IE 中沒有觸發(fā) layout 的功能。
overflow-x|-y: hidden|scroll|auto
- overflow-x 和 overflow-y 是 CSS3 盒模型中的屬性,尚未得到瀏覽器的廣泛支持。他們在之前版本IE中沒有觸發(fā) layout 的功能。
另外 IE7 的熒幕上又新添了幾個 haslayout 的演員,如果只從 hasLayout 這個方面考慮,min/max 和 width/height 的表現(xiàn)類似,position 的 fixed 和 absolute 也是一模一樣。
position: fixed
- ./.
min-width: 任意值
- 就算設(shè)為0也可以讓該元素獲得 layout。
max-width: 除 “none” 之外的任意值
- ./.
min-height: 任意值
- 即使設(shè)為0也可以讓該元素的 haslayout=true
max-height: 除 “none” 之外的任意值
- ./.
以上結(jié)論借助 IE Developer Toobar 以及預(yù)先測試得出。
有關(guān)內(nèi)聯(lián)級別元素
對于內(nèi)聯(lián)元素(可以是默認(rèn)即為內(nèi)聯(lián)的比如 span
元素,也可以是 display: inline
的元素)
width
和 height
只在 IE5.x 下和 IE6 或更新版本的 quirks 模式下觸發(fā) hasLayout
。而對于 IE6,如果瀏覽器運(yùn)行于標(biāo)準(zhǔn)兼容模式下,內(nèi)聯(lián)元素會忽略 width 或 height 屬性,所以設(shè)置 width 或 height 不能在此種情況下令該元素具有 layout。
zoom
總是可以觸發(fā) hasLayout
,但是在 IE5.0 中不支持。
具有“l(fā)ayout” 的元素如果同時也 display: inline
,那么它的行為就和標(biāo)準(zhǔn)中所說的 inline-block 很類似了:在段落中和普通文字一樣在水平方向和連續(xù)排列,受 vertical-align 影響,并且大小可以根據(jù)內(nèi)容自適應(yīng)調(diào)整。這也可以解釋為什么單單在 IE/Win 中內(nèi)聯(lián)元素可以包含塊級元素而少出問題,因?yàn)樵趧e的瀏覽器中 display: inline
就是內(nèi)聯(lián),不像 IE/Win 一旦內(nèi)聯(lián)元素?fù)碛?layout 還會變成 inline-block。
腳本屬性 hasLayout
我們這里稱 hasLayout 為“腳本屬性”是為了和我們熟知的 CSS 屬性相區(qū)別。
注意一旦一個元素?fù)碛辛?layout,就沒有辦法再將其設(shè)成 hasLayout = False
了。
hasLayout-property 可以用來檢測一個元素是否擁有 layout:舉個例子,如果它的 id
是“eid”,那么只要在 IE5.5+ 的地址欄里輸入 javascript: alert(eid.currentStyle.hasLayout)
即可檢測它的狀態(tài)。
IE的 Developer Toolbar 可以實(shí)時檢查一個元素的當(dāng)前樣式;如果 hasLayout
是 true ,那么它的值顯示為 “-1”。 我們可以通過實(shí)時修改一個元素的屬性將“zoom(css)”設(shè)置為“1”來觸發(fā) hasLayout
以便調(diào)試。
另外一個需要注意的是“l(fā)ayout”會影響腳本編程。如果一個元素沒有“l(fā)ayout”,那么clientWidth
/clientHeight
總是返回0。這會讓一些腳本新手感到困惑,而且這和 Mozilla 瀏覽器的處理方式也不一樣。不過我們可以利用這一點(diǎn)在 IE5.0 中檢測“l(fā)ayout”:如果 clientWidth
是零那么這個元素就沒有 layout。
CSS hacks
下面用于觸發(fā) haslayout
的 hack 已經(jīng)經(jīng)過 IE6 及以下版本測試。今后版本的IE有可能會對此做不同處理。如果新版本瀏覽器發(fā)布我們會重新整理這部分內(nèi)容。
John Gallant 和 Holly Bergevin 在2003年發(fā)布的 Holly hack :
/* \*/
* html .gainlayout { height: 1%; }
/* */
- 可以讓 IE5+ 的任意元素獲得 layout,除了標(biāo)準(zhǔn)兼容模式 IE6 中的內(nèi)聯(lián)元素。
- 一般都很有效,除了在某些極少情況下,需要用 height:0 或者 1px 更好一些。
- 和
overflow: hidden
不相容,除非在 IE6 的標(biāo)注兼容模式下(因?yàn)檫@時如果父元素沒有定高,那么height: 1%
會被變回 height: auto
)。
或者我們可以用 underscore hack:
.gainlayout { _height: 0; }
另外,更具有向后兼容性的方法是使用 條件注釋(conditional comments):
!--[if lte IE 6]>
style>
.gainlayout { height: 1px; }
/style>
![endif]-->
在條件注釋中鏈接一個專門對 IE/Win 做修正的外部樣式表文件,也不失為一個安全有效的好方法:
link rel="stylesheet" href="allbrowsers.css" type="text/css" />
!--[if lte IE 6]>
link rel="stylesheet" href="iefix.css" type="text/css" />
![endif]-->
我們更傾向于使用 height: 0
和 1px
—— 并主張始終使用 height
除非它和別的什么東西沖突 (overflow: hidden
)。對于取值,我們則傾向于避免 1%
,因?yàn)樗赡軙?雖然很少)引起一些問題。
height
不能應(yīng)用于標(biāo)準(zhǔn)模式下的內(nèi)聯(lián)元素。在這種情況下我們可以用 display: inline-block
或 zoom: 1
。
我們曾看過一些把 Holly hack 真的當(dāng)作 holy(神圣的) hack 盲目使用的情況,比如對浮動元素使用或者對已經(jīng)具有特定寬度的元素也使用這個 hack。要記住這個 hack 的目的不是要給某個元素加一個高度,而只是要觸發(fā) hasLayout = True
而已。
不要給所有元素設(shè)置 layout:* {_height: 1px;}
。所謂過猶不及,獲得 layout 不等于獲得靈丹妙藥,它只是用來改變渲染模式。
Hack整理
但是瀏覽器總是會變的,我們需要面對很多問題,比如一些依賴 IE6 的 bug 所做的 hack 會在 IE7 或更高版本的新瀏覽器中因 bug 修復(fù)而失效(甚至有害)的問題;比如新版本瀏覽器中類似的布局 bug 依然存在但用于 hack 的過濾器比如 * html
卻不能正常工作的問題。這種情況下,MS專有屬性 zoom
就可以考慮使用了。
!--[if lt IE 7]>style>
/* IE 6 + IE5.5 + IE5.0 所用樣式*/
.gainlayout { height: 0; }
/style>![endif]-->
!--[if IE 7]>style>
.gainlayout { zoom: 1;}
/* 或者其他任何以后可能需要的東西 */
/style>![endif]-->
zoom: 1;
可以讓 IE5.5+ 的任何元素(包括內(nèi)聯(lián)元素)獲得 layout,但是在 IE5.0 中無效。
- 沒有其他附帶效果(內(nèi)聯(lián)元素會變成 inline-block,這個當(dāng)然)。
- 如果需要通過驗(yàn)證,應(yīng)該用條件注釋將
zoom
隱藏起來。
其實(shí)當(dāng)我們考慮到“向后兼容”時是很自相矛盾的,我們強(qiáng)烈建議頁面設(shè)計(jì)者回過頭看一下自己頁面中用的到的明顯的或是不明顯的“hacks”,并用條件注釋針對不同瀏覽器重新處理以保萬無一失。
關(guān)于IE Mac 的小問題
IE Mac 和 windows 下的 IE 是完全不同的兩個東西,它們各自擁有自己的渲染引擎,IE Mac 就全然不知“hasLayout”(或contenteditable)所謂何物。相比之下 IE Mac 的渲染引擎要更標(biāo)準(zhǔn)兼容一點(diǎn),比如 height
就是被當(dāng)作 height
處理,沒有別的效果。因此針對“hasLayout”的 hacks 和別的解決方法(特別是通過使用 height
或 width
屬性的)往往對 IE Mac 來說是有害的,所以需要對其隱藏。更多的關(guān)于 IE Mac 相關(guān)的問題可以在 IE Mac, bugs and oddities pages 找到。
MSDN 文檔
MSDN 中涉及到 hasLayout 這個 MS 屬性的地方寥寥無幾,而具體解釋 layout 和 IE 渲染模型之間關(guān)系的則少之又少。
在IE4的時候,除了未經(jīng)絕對定位也未指定寬高的內(nèi)聯(lián)元素,幾乎所有元素都有某種 layout(MSDN)。在這種早期的layout概念中,像 border, margin, padding
這些屬性被稱作“l(fā)ayout屬性”,它們是不能應(yīng)用到一個簡單的內(nèi)聯(lián)元素上的。換句話說,“擁有l(wèi)ayout”就可以粗略理解成:“可以擁有這幾個屬性”。
MSDN 上仍然使用 layout 屬性這種說法, 只是含義變了,它們和擁有 layout 的元素已經(jīng)沒有什么關(guān)系了。在 IE5.5 中方才引入了 MS 的這個專有屬性 hasLayout
,也只是某種內(nèi)部的標(biāo)志位而已。
在 IE5.5 中,MSHTML Editing Platform(即可以通過設(shè)置body contenteditable=true>
來允許用戶實(shí)時編輯、拖動 layout 元素以及調(diào)整其尺寸等)的文檔中描述了三個和 layout 相關(guān)的重要特性:
如果一個 layout 元素中有內(nèi)容,內(nèi)容的排版布局將由它的邊界矩形框決定。
擁有 layout 的意思基本上就是表示某元素是一個矩形。
從內(nèi)部來說,擁有 layout 意思就是一個元素將自己負(fù)責(zé)繪制其內(nèi)部內(nèi)容。
(Editing Platform)
和 layout 自身相關(guān)的內(nèi)部工作機(jī)制直到2005年8月才有相應(yīng)文檔描述,當(dāng)時由于 The Web Standards Project 和微軟的特別工作小組的原因,Markus Mielke [MSFT] 打開了深入討論的大門:
一般來說,在 Internet Explorer 的 DHTML 引擎中,元素是不對自己的位置安排負(fù)責(zé)的。雖然一個 div 或者一個 p 元素都在源碼中有一個位置,在文檔流有一個位置,但是它們的內(nèi)容卻是由它們最近的一個 layout 祖先(經(jīng)常是 body)控制安排的。這些元素依賴它們祖先的 layout 來為他們處理諸如決定大小尺寸和測量信息等諸多繁重的工作。
(HasLayout概述)
分析
我們的分析試圖解釋在已知案例下發(fā)生了什么事情,這種分析也應(yīng)該可以作為未知案例下的指導(dǎo)。但我們這種試圖利用種種測試案例投石探路的黑箱測試方法,是注定無法消除黑箱的神秘感的——我們無法回答“為什么”的問題。我們只能去嘗試了解整個“hasLayout”模式的工作框架,以及它會怎樣影響網(wǎng)頁文檔的渲染。因此,最終我們只能提供一些指導(dǎo)方針(而且只能是指導(dǎo)方針,而不是絕對的解決方案)。
我們認(rèn)為他們所指的是一個小窗體。一個 layout 元素的內(nèi)部內(nèi)容是完全獨(dú)立的,而且也無法影響其邊界外的任何內(nèi)容。
而 MS 屬性 layout 只是某種標(biāo)志位:一旦它被設(shè)定,這個元素就會擁有 layout“特性”,這包括體現(xiàn)在其自身以及其非 layout 孩子元素身上的特殊性能——比如浮動和層疊等。
這種獨(dú)立性也許正可以解釋為什么 layout 元素通常比較穩(wěn)定,而且它們可以讓某些 bug 消失。這種情況的代價有二,一是偏離了標(biāo)準(zhǔn),二是它沒有考慮到今后可能因此出現(xiàn)的 bug 和問題。
MS 的“頁面”模式,從符號學(xué)角度考慮,可以看做是由很多互不相關(guān)的小的區(qū)塊構(gòu)成,而 HTML 和 W3C 的模式則認(rèn)為“頁面”模式應(yīng)該是敘述完備的,故事性的相關(guān)信息區(qū)塊構(gòu)成的。
各種情況的詳細(xì)說明
清除浮動和自動擴(kuò)展適應(yīng)高度
浮動元素會被 layou 元素自動包含。這是很多新手經(jīng)常遇到的問題:在 IE 下完成的頁面到了標(biāo)準(zhǔn)兼容瀏覽器下所有未清除的浮動元素都伸出了其包含容器之外。
- Containing Floats
- how to clear floats without structural markup
相反的情況:如果確實(shí)需要一個浮動元素伸出其包含容器,也就是自動包含不是想要的效果時,該怎么辦?你很可能也會遇到這種頭疼的問題,下面的深入討論就是一個例子:
在IE中,一個浮動元素總是“隸屬于”它的 layout 包含容器。而后面的元素會受這個 layout 包含容器影響而不是這個浮動元素影響。
這個特性和IE6的那個自動擴(kuò)展以適應(yīng)內(nèi)部內(nèi)容寬度的特性,都可以看成是受這個規(guī)則影響的:“由它的邊界矩形框決定”。
更糟的問題:clear
無法影響其 layout 包含容器之外的 float 元素。如果依賴這個 bug 在 IE 中布局的頁面要轉(zhuǎn)到標(biāo)準(zhǔn)兼容瀏覽器中,只有全部重做。
IE 的自動包含浮動元素也是經(jīng)常需要的效果,它在其他瀏覽器中也可以達(dá)到:參考我們的 “和 CSS 規(guī)范類似的地方” 這一部分來了解一下包含浮動元素的相關(guān)內(nèi)容。
浮動元素旁邊的元素
當(dāng)一個塊級元素緊跟在一個左浮動元素之后時,它應(yīng)該——作為一個塊級元素——忽略這個浮動元素,而它的內(nèi)容則應(yīng)該因這個浮動元素而移位:一個緊跟在左浮動元素后的塊級元素內(nèi)的文字內(nèi)容,應(yīng)該沿著浮動元素的右邊順序排列并會(如果它的長度超過浮動元素)繼續(xù)排列到浮動元素下方。但是如果這個塊級元素有 layout,比如由于某種原因被設(shè)置了寬度,那么這整個元素則會因浮動元素而移位,就好像它自己也是一個浮動元素一樣,因此其中的文字就不再環(huán)繞這個左浮動元素了(而會形成一個矩形區(qū)域,保持在它的右邊。)
在 IE5 中一個塊級元素的百分比寬度是基于浮動元素旁邊的剩余空間計(jì)算的,而在 IE6 中則是依照整個父塊級元素的可用空間計(jì)算的。所以在 IE6 中設(shè)置 width: 100%
會導(dǎo)致某種浮動元素旁邊的溢出現(xiàn)象,于是各種布局問題也會因此而來。
一些關(guān)于浮動塊旁邊的 hasLayout 塊的測試案例:
- by using width
- by using min-width (IE 7) and zoom (IE 6)
與此類似,和浮動元素相鄰的相對定位元素,它的位置偏移量應(yīng)該參照的是父元素的補(bǔ)白(padding)邊緣(例如,left: 0;
應(yīng)該將一個相對定位元素疊放于它前面的浮動元素之上)。在 IE6 中,偏移量 left: value;
是從浮動元素的右邊距(margin)邊緣開始算起的,這會因浮動元素所占的寬度變化導(dǎo)致水平方向的錯位(一個解決方法是用 margin-left
代替,但是也要注意如使用百分值時會有一些怪異問題)。
- layout blocks with relative positioning adjacent to floated blocks
根據(jù)規(guī)范所述,浮動元素應(yīng)該與其后的盒子交織在一起。而對于沒有交叉的二維空間中的矩形而言這是無法實(shí)現(xiàn)的。
如果誰真的需要向 IE 的這種不當(dāng)行為屈服,那么如何讓標(biāo)準(zhǔn)兼容瀏覽器中的盒子也有類似行為——即類似于 layout 盒子會自動“收縮”而給其前置的浮動元素讓出空間的行為——就是一個問題了。我們給出的方法是跟著一個浮動元素創(chuàng)建一個新的塊級格式化范圍(block formatting context),這在我們的“和 CSS 規(guī)范類似的地方” 有討論。
可以(再次)訪問下面這個頁面:
我們可以看到跟在一個浮動元素后的 layout 元素不會顯示這個3px間隙的 bug,因?yàn)楦釉赝鈬?px硬邊無法影響一個 layout 元素的內(nèi)部內(nèi)容,所以這個硬邊將整個 layout 元素右推了3px。好比一個防護(hù)罩,layout 可以保護(hù)其內(nèi)部內(nèi)容不受影響,但是浮動元素的力量卻將整個防護(hù)罩推了開來。
列表
無論是列表本身(ol, ul
) 還是單個的列表元素(li
),擁有 layout 后都會影響列表的表現(xiàn)。不同版本 IE 的表現(xiàn)又有不同。最明顯的效果就體現(xiàn)在列表符號上(如果你的列表自定義了列表符號則不會受這個問題影響)。這些符號很可能是通過某種內(nèi)部機(jī)制附到列表元素上的(通常是附著在它們外面)。不幸的是,由于是通過“內(nèi)部機(jī)制”添加的,我們無法訪問它們也無法修正它們的錯誤表現(xiàn)。
最明顯的效果有:
- 列表獲得 layout 后,列表符號會消失或者被放置在不同的或者錯誤的位置。
有時它們又可以通過改變列表元素的邊距而重新出現(xiàn)。這看起來似乎是以下事實(shí)導(dǎo)致的結(jié)果:layout 元素會試圖裁掉超出其邊界的內(nèi)部內(nèi)容。
- 列表元素獲得 layout 之后,會有和上面一樣的問題出現(xiàn),更多參考 (extra vertical space between list items)
進(jìn)一步又有一個問題就是(在有序列表中)任何具有 layout 的列表元素似乎都有自己獨(dú)立的計(jì)數(shù)器。比如我們有一個含五個列表元素的有序列表,只有第三個列表元素有 layout。我們會看到這樣:
1… 2… 1… 4… 5…
此外,如果一個有 layout 的列表元素跨行顯示時,列表符號會底部對齊(而不是按照預(yù)料的頂部對齊)。
以上某些問題還是無法解決的,所以如果需要列表符號的時候最好避免讓列表和列表元素獲得 layout。如果需要限定尺寸,最好給別的元素設(shè)定尺寸,比如給整個列表外面套一個元素并設(shè)定它的寬度,又或者比如給每個列表元素中的內(nèi)容設(shè)定高度等等。
另一個IE中列表的常見問題出現(xiàn)在當(dāng)某個 li
中的內(nèi)容是一個 display: block
的錨點(diǎn)(anchor)時。在這種情況下,列表元素之間的空格將不會被忽略而且通常會顯示成額外的一行夾在每個 li
之間。一種避免這種豎直方向多余空白的解決方法是賦予這些錨點(diǎn) layout。這樣還有一個好處就是可以讓整個錨點(diǎn)的矩形區(qū)域都可以響應(yīng)鼠標(biāo)點(diǎn)擊。
表格
table
總是有 layout 的,它總表現(xiàn)為一個已定義寬度的對象。在IE6中,table-layout: fixed
通常和一個寬度設(shè)為100%的表格相同,同時這也會帶來很多問題(一些計(jì)算方面的錯誤)。另外在IE5.5和IE6的quirks模式下還有一些別的需要注意的情況。
相對定位元素(r.p.)
注意,由于 position: relative
并不觸發(fā) hasLayout,所以很多諸如內(nèi)容消失或錯位的渲染錯誤就會因此而起。這些現(xiàn)象可能會在刷新頁面、調(diào)整窗口大小、滾動頁面、選中內(nèi)容等情況下出現(xiàn)。原因是 IE 在據(jù)這個屬性對元素做偏移處理時,卻似乎忘了發(fā)出信號讓其 layout 孩子元素進(jìn)行“重繪”(而如果是一個layout元素,那么在其重繪事件的信號鏈中,這個傳給其孩子的信號是會正常發(fā)出的)。
- r.p. parent and disappearing floated child
- disappearing list-background bug
以上是一些相關(guān)問題的描述。作為經(jīng)驗(yàn)之談,相對定位一個元素時最好給予其 layout。再有,我們也需要檢查擁有這種結(jié)構(gòu)的父元素是否也需要 layout 或者position: relative
亦或二者都需要,如果涉及到浮動元素這點(diǎn)就十分重要。
絕對定位元素(a.p.):
包含區(qū)塊,什么是包含區(qū)塊?
理解 CSS 的包含區(qū)塊概念很重要,它回答了絕對定位元素是相對哪里定位的問題:包含區(qū)塊決定了偏移起點(diǎn),包含區(qū)塊定義了百分比長度的計(jì)算參考。
對于絕對定位元素,包含區(qū)塊是由其最近的定位祖先決定的。如果其祖先都沒有被定位,那么就使用初始包含區(qū)塊 html
。
通常情況下我們會用 position: relative
來設(shè)定任意包含區(qū)塊。這就是說,我們可以讓一個絕對定位元素所參考的原點(diǎn)和長度等不依賴于元素的排列順序,這可以滿足諸如“內(nèi)容優(yōu)先”這種可訪問性概念的需要,也可以給復(fù)雜的浮動布局帶來方便。
但是由于 layout 概念的存在,這種設(shè)計(jì)理念的效果在IE中就要打個問號了:因?yàn)樵贗E中絕對定位只有當(dāng)其包含元素?fù)碛?layout 時才會計(jì)算正確,而且絕對定位元素的百分比寬度參考也搞錯了對象。這里 IE5 和 IE6 的行為不同但都有問題。IE7b2 的行為就要好很多,雖然有些小地方還是有錯誤。總之盡可能的讓絕對元素的包含區(qū)塊擁有 layout,而且盡量讓其就是絕對定位元素的父級元素(也就是說這個包換元素和絕對定位元素之間沒有絕對定位元素的別的祖先了)。
假設(shè)一個無 layout 的父元素被相對定位了——我們就得給它賦予 layout 才能使偏移量起作用:
假設(shè)一個未定位的父元素需要特定尺寸,而且頁面設(shè)計(jì)是基于百分比寬度的——我們就可以放棄這個想法了,因?yàn)闉g覽器支持不佳:
- absolutely positioned element and percentage width
濾鏡
MS專有的濾鏡屬性 filter 是只適用于 layout 元素的。有些濾鏡擴(kuò)展了對象的邊界。它們會顯示出自身特有的缺陷。
對已渲染元素的重排(re-flow)
當(dāng)所有元素都已渲染完成時,如果有一個因鼠標(biāo)經(jīng)過而引起的變化產(chǎn)生(比如某個鏈接的 background
有變化),IE會對其 layout 包含區(qū)塊進(jìn)行重排。有時一些元素就會因此被排到了新的位置,因?yàn)楫?dāng)這個鼠標(biāo)經(jīng)過發(fā)生時,IE已經(jīng)知道了所有相關(guān)元素的寬度、偏移量等數(shù)據(jù)了。這在文檔首次載入時則不會發(fā)生,那時由于自動擴(kuò)張的特性,寬度還無法確定。這種情況會導(dǎo)致在鼠標(biāo)經(jīng)過時頁面出現(xiàn)跳變。
- Jump on :hover
- quirky percentages: the reflow
這些和重排問題相關(guān)的 bug 會給百分比邊距和補(bǔ)白使用較多的流動布局帶來不少麻煩。
背景原點(diǎn)
MS專有的這個 hasLayout 還會影響背景的定位和擴(kuò)展。比如,根據(jù) CSS 規(guī)范,background-position: 0 0
應(yīng)該指元素的“補(bǔ)白邊緣(padding edge)”。而在 IE/Win 下,如果 hasLayout = false
則指的是“邊框邊緣(border edge)”,當(dāng) hasLayout=true
時指的才是補(bǔ)白邊緣:
- Background, Border, hasLayout
邊距重疊
hasLayout
會影響一個盒子和其子孫的邊距重疊。根據(jù)規(guī)范,一個盒子如果沒有上補(bǔ)白和上邊框,那么它的上邊距應(yīng)該和其文檔流中的第一個孩子元素的上邊距重疊:
- Collapsing Margins
- Uncollapsing Margins
在 IE/Win 中如果這個盒子有 layout 那么這種現(xiàn)象就不會發(fā)生了:似乎擁有 layout 會阻止其孩子的邊距伸出包含容器之外。此外當(dāng) hasLayout = true
時,不論包含容器還是孩子元素,都會有邊距計(jì)算錯誤的問題出現(xiàn)。
- Margin collapsing and hasLayout
塊級別的鏈接
hasLayout 會影響一個塊級別鏈接的鼠標(biāo)響應(yīng)區(qū)域(可點(diǎn)擊區(qū)域)。通常 hasLayout = false 時只有文字覆蓋區(qū)域才能響應(yīng)。而 hasLayout = true 則整個塊狀區(qū)域都可響應(yīng)。添加了 onclick/onmouseover 等事件的任意塊級元素也有同樣的現(xiàn)象。
- Block anchors and hasLayout
在頁面內(nèi)使用鍵盤瀏覽:探索中
當(dāng)使用 tab 在頁面中瀏覽時,如果進(jìn)入了一個頁內(nèi)鏈接(in-page link),那么接下來再按的 tab 鍵就不會正常繼續(xù)了:
- hasLayout Property Characterizes IE6 Bug
- Keyboard Navigation and Internet Explorer
tab 鍵會把用戶帶到(這通常是錯誤的)其最近的 layout 祖先中的第一個目標(biāo)(如果這個祖先是由 table
, div
, span
或某些別的標(biāo)簽構(gòu)成)。
收縮包圍(shrink-wrapping)現(xiàn)象
給已經(jīng)有 width: auto
的元素添加某些屬性會導(dǎo)致它們在計(jì)算自身寬度時使用一種收縮包圍的算法。比如這些屬性 float: left|right, position: absolute|fixed, display: table|table-cell|inline-block|inline-table.
這些屬性造成的現(xiàn)象在IE/Win中也存在,當(dāng)然這是只對那些它支持的屬性而言。但是當(dāng)一個應(yīng)該收縮包圍的元素中包含一個擁有“l(fā)ayout”的塊級元素時,在絕大多數(shù)情況下,這個孩子元素的寬度會盡可能地?cái)U(kuò)展而與其中包含的內(nèi)容無關(guān),同時也阻止了父元素的收縮包圍現(xiàn)象。
- 例子:
- 一個浮動的縱向?qū)Ш綗o序列表并沒有收縮包圍,因?yàn)槠渲械逆溄訛榱讼斜淼亩嘤嗫瞻譩ug并擴(kuò)展可點(diǎn)擊區(qū)域而擁有了 layout:
a {display: block; zoom: 1;}
。
這時收縮包圍現(xiàn)象只有在以下情況仍然有效:擁有 layout 的孩子元素同時也被賦予了一個特定寬度,或者這個孩子元素本身也是一個具有收縮包圍特性的元素,比如浮動元素。
邊緣裁切
通常而言,當(dāng)一個盒子包含了諸如伸出其邊緣的內(nèi)容這種更復(fù)雜的結(jié)構(gòu)時,這個容器就經(jīng)常需要“hasLayout”來避免一些渲染錯誤。但使用這種常用方法又會在邊界處理時左右為難,因?yàn)橐粋€獲得“l(fā)ayout”的元素會變成某種自封閉的盒子。
內(nèi)部的內(nèi)容盒子會被裁切,比如使用負(fù)邊距向外移動時。
- Clipping of negative margined blocks in a hasLayout container
被裁掉的部分當(dāng)內(nèi)容盒子觸發(fā)了“l(fā)ayout”時可以再次出現(xiàn),但在 IE6 中需要同時擁有 position: relative
才行。IE7 在這方面要略有改觀,它不再需要額外的 position: relative
了。
堆疊,分層和 layout
IE/Win 中似乎有兩種分層和堆疊順序:
- 一種是(偽)試圖采用CSS的模式:Effect of z-index value to RP and AP blocks
- 還有一種是由“hasLayout”及其孿生兄弟“contenteditable”的行為產(chǎn)生的堆疊順序。正如在上面相對定位的例子中展現(xiàn)的那樣,在 layout 影響下的堆疊現(xiàn)象就好像 Harry Houdini (譯者注:魔術(shù)師,以紙牌魔術(shù)成名)的拿手戲法兒一樣。
兩種堆疊模式雖互不相容,但卻共存于IE的渲染引擎中。經(jīng)驗(yàn)之談:調(diào)試的時候,兩種情況都要考慮到。我們可能會有規(guī)律地在下拉菜單或者類似的復(fù)雜菜單中看到相關(guān)問題,因?yàn)樗鼈兺鶢可娴蕉询B,定位和浮動等諸多令人頭疼的問題。給那些 z-index 定位的元素 layout 是一種可能的修正方法,不過也不限于此,這里只是提醒一下。
混亂的 contenteditable
如果給一個 HTML 標(biāo)簽設(shè)定 contenteditable=true
屬性,比如body contenteditable=true>
,將會允許對該元素以及其 layout 子元素進(jìn)行實(shí)時的編輯、拖動改變尺寸等操作。你可以把這屬性用在浮動元素或者一個有序列表中的 layout 列表元素上看看效果。
為了對元素進(jìn)行操作(編輯它們),“contenteditable”和“hasLayout”為那些 hasLayout
返回 true
的元素引入了一套單獨(dú)的堆疊順序。
Editing Platform 繼承了 layout 概念,對 layout 的誤解多是因 contenteditable 而起即可作為證明(那些某種程度上集成了IE編輯引擎的應(yīng)用軟件多暗含著對layout概念的某種強(qiáng)制向后兼容性)。
和 CSS 規(guī)范類似的地方
你的 MSIE 頁面在別的瀏覽器中一團(tuán)糟?我們可沒必要讓這種事情發(fā)生。如果使用恰當(dāng),任何好的瀏覽器都能擺平 MSIE 的頁面——只要你使用一些正確的 CSS。
利用 hasLayout
和“新的塊級格式化范圍”之間的細(xì)微相似之處,我們可以有幾種方法在標(biāo)準(zhǔn)兼容瀏覽器中重新實(shí)現(xiàn) hasLayout
的“包含浮動元素”效果,和一些“浮動元素旁邊的元素”所特有的效果。
- Reverse engineering series
- Simulations
Quirks 模式
關(guān)于這種渲染模式的的信息,請參考我們的 quirks 模式章節(jié)。
Layout ——結(jié)論
整個 layout 概念和一些基本 CSS 概念是不兼容的,即包含,排列,浮動,定位和層疊等。
由于頁面中元素或有或沒有 layout,會導(dǎo)致 IE/Win 的行為和 CSS 規(guī)范相違背。
擁有 layout ——另外一個引擎?
IE 的對象模型看起來是文檔模型和他們傳統(tǒng)的應(yīng)用程序模型的糅合。我之所以提到這點(diǎn)是因?yàn)樗鼘τ诶斫釯E如何渲染頁面很重要。而從文檔模型切換到應(yīng)用程序模型的開關(guān)就是給一個元素“l(fā)ayout”。
(Dean Edwards)
有時候要解釋清楚某種行為是不可能的:就比如 hasLayout,會根據(jù)它的狀態(tài)選擇兩種不同渲染引擎的一種使用,而且每一種都有其自己的 bug 和怪異之處。
不可消除的 bug
軟件 bug 是由于在制作過程中對完整性和邏輯問題考慮不周等人為錯誤而導(dǎo)致的。這是人類的固有缺陷,目前還沒有什么好的解決方法。
同樣由于這種缺陷,任何試圖不重寫軟件而修復(fù) bug 的做法,都將會不可避免的導(dǎo)致軟件中出現(xiàn)更復(fù)雜的bug。
所有依賴別的軟件的軟件——當(dāng)然包括依賴操作系統(tǒng),也會同樣依賴他們的 bug。于是我們會從所有關(guān)聯(lián)的軟件中得到一連串的 bug,這也更說明找到一個無 bug 軟件是幾乎不可能的。
(Molly, the cat‛)
本文創(chuàng)建于2005年6月30日,最后一次修改于2006年4月2日。
- 編者:
- Holly Bergevin
- Ingo Chao
- Bruno Fassino
- John Gallant
- Georg Sørtun
- Philippe Wittenbergh
- 特別致謝給予此項(xiàng)目支持的:
- Dean Edwards, and Molly ‚the cat‛
- 各種語言版本:
- Original(English)
- Brazilian Portuguese by Mauricio Samy Silva
- 中文版本 by old9
- Italian by Gabriele Romanato
- 相關(guān)討論:
- dean.edwards.name/weblog/
- 聯(lián)系作者:
spam.layout@satzansatz.de
- 版權(quán)說明:
- 本文基于創(chuàng)作共用協(xié)議發(fā)布。