譯者注:一篇很好的文章,很久以前在blog上就推薦過,這兩天斷斷續(xù)續(xù)花了點時間翻譯了一下,推薦讀讀。英文原文在此。
文中所有的 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)
介紹
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)用程序事件/用戶事件等,這有點類似于一個窗體的概念。
微軟的開發(fā)者們認(rèn)為盒狀元素(box-type elements)應(yīng)該具有一個“屬性(property)”(這是面向?qū)ο缶幊讨械囊粋€概念),于是他們便使用了 layout
, 也就是 hasLayout
。
hasLayout
其實既不是一個屬性更不是一個行為,而是 IE 這個渲染引擎代代繼承一貫擁有的一個渲染概念,在這個概念下渲染的元素將具有一種特性。
實際上這種渲染特性在有些 HTML 元素中與身俱來,而在另外一些元素中也可以通過一些 CSS 屬性將其觸發(fā)為 true
,且一旦觸發(fā)將不可逆轉(zhuǎn)。
術(shù)語
當(dāng)我們說一個元素“擁有l(wèi)ayout”或“得到layout”,或者說一個元素“has 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è)計師或者程序員可能都遇到過。具有 layout 的元素通常有著不同尋常而且難以預(yù)料的的顯示效果,而且有時甚至?xí)窟B到他們的孩子元素。
一個元素是否具有“l(fā)ayout”可能會引發(fā)如下的一些問題:
- IE 很多常見的浮動 bug 。
- 元素本身對一些基本屬性的異常處理問題。
- 容器和其子孫之間的邊距重疊(margin collapsing)問題。
- 使用列表時遇到的諸多問題。
- 背景圖像的定位偏差問題。
- 使用腳本時遇到的瀏覽器之間處理不一致的問題。
上面的列表只是列出一個大概,也不完善。下面的文章將盡可能詳細徹底的描述有無“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 屬性的唯一效果——讓某個元素擁有 layout?!癷nline-block行為”在IE中是可以實現(xiàn)的,但是非常與眾不同: IE/Win: inline-block and hasLayout 。
width: 任意值
- 很多人遇到 layout 相關(guān)問題發(fā)生時,一般都會先嘗試用這個來修復(fù)。
height: 任意值
- height: 1% 就在 Holly Hack 中用到。
zoom: 任意值
(MSDN)
- MS專有屬性,無法通過校驗。 不過
zoom: 1
可以臨時用做調(diào)試。
writing-mode: tb-rl
(MSDN)
- MS專有屬性,無法通過校驗。
在 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 中,如果瀏覽器運行于標(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)元素可以包含塊級元素而少出問題,因為在別的瀏覽器中 display: inline
就是內(nèi)聯(lián),不像 IE/Win 一旦內(nèi)聯(lián)元素擁有 layout 還會變成 inline-block。
腳本屬性 hasLayout
我們這里稱 hasLayout 為“腳本屬性”是為了和我們熟知的 CSS 屬性相區(qū)別。
注意一旦一個元素擁有了 layout,就沒有辦法再將其設(shè)成 hasLayout = False
了。
hasLayout-property 可以用來檢測一個元素是否擁有 layout:舉個例子,如果它的 id
是“eid”,那么只要在 IE5.5+ 的地址欄里輸入 javascript: alert(eid.currentStyle.hasLayout)
即可檢測它的狀態(tài)。
IE的 Developer Toolbar 可以實時檢查一個元素的當(dāng)前樣式;如果 hasLayout
是 true ,那么它的值顯示為 “-1”。 我們可以通過實時修改一個元素的屬性將“zoom(css)”設(shè)置為“1”來觸發(fā) hasLayout
以便調(diào)試。
另外一個需要注意的是“l(fā)ayout”會影響腳本編程。如果一個元素沒有“l(fā)ayout”,那么clientWidth
/clientHeight
總是返回0。這會讓一些腳本新手感到困惑,而且這和 Mozilla 瀏覽器的處理方式也不一樣。不過我們可以利用這一點在 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)注兼容模式下(因為這時如果父元素沒有定高,那么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%
,因為它可能會(雖然很少)引起一些問題。
一個需要注意的情況是如果我們希望一個元素保持內(nèi)聯(lián),那么就不能使用 height
了,這時可以用 display: inline-block
。我們只在早期調(diào)試階段用 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īng)該用條件注釋將
zoom
隱藏起來。
其實當(dāng)我們考慮到“向后兼容”時是很自相矛盾的,我們強烈建議頁面設(shè)計者回過頭看一下自己頁面中用的到的明顯的或是不明顯的“hacks”,并用條件注釋針對不同瀏覽器重新處理以保萬無一失。
關(guān)于IE Mac 的小問題
IE Mac 和 windows 下的 IE 是完全不同的兩個東西,它們各自擁有自己的渲染引擎,IE Mac 就全然不知“hasLayout”(或contenteditable)所謂何物。相比之下 IE Mac 的渲染引擎要更標(biāo)準(zhǔ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>
來允許用戶實時編輯、拖動 layout 元素以及調(diào)整其尺寸等)的文檔中描述了三個和 layout 相關(guān)的重要特性:
如果一個 layout 元素中有內(nèi)容,內(nèi)容的排版布局將由它的邊界矩形框決定。
擁有 layout 的意思基本上就是表示某元素是一個矩形。
從內(nèi)部來說,擁有 layout 意思就是一個元素將自己負責(zé)繪制其內(nèi)部內(nèi)容。
(Editing Platform)
和 layout 自身相關(guān)的內(nèi)部工作機制直到2005年8月才有相應(yīng)文檔描述,當(dāng)時由于 The Web Standards Project 和微軟的特別工作小組的原因,Markus Mielke [MSFT] 打開了深入討論的大門:
一般來說,在 Internet Explorer 的 DHTML 引擎中,元素是不對自己的位置安排負責(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)容是完全獨立的,而且也無法影響其邊界外的任何內(nèi)容。
而 MS 屬性 layout 只是某種標(biāo)志位:一旦它被設(shè)定,這個元素就會擁有其特殊的 layout“特性”,這包括體現(xiàn)在其自身以及其非 layout 孩子身上的浮動、清除浮動、層疊、計數(shù)等等諸多方面的特殊性能。
這種獨立性也許正可以解釋為什么 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)成的。
各種情況的詳細說明
清除浮動和自動擴展適應(yīng)高度
浮動元素會被 layou 元素自動包含。這是很多新手經(jīng)常遇到的問題:在 IE 下完成的頁面到了標(biāo)準(zhǔn)兼容瀏覽器下所有未清除的浮動元素都伸出了其包含容器之外。
- Containing Floats
- how to clear floats without structural markup
相反的情況:如果確實需要一個浮動元素伸出其包含容器,也就是自動包含不是想要的效果時,該怎么辦?你很可能也會遇到這種頭疼的問題,下面的深入討論就是一個例子:
在IE中,一個浮動元素總是“隸屬于”它的 layout 包含容器。而后面的元素會受這個 layout 包含容器影響而不是這個浮動元素影響。
這個特性和IE6的那個自動擴展以適應(yīng)內(nèi)部內(nèi)容寬度的特性,都可以看成是受這個規(guī)則影響的:“由它的邊界矩形框決定”。
更糟的問題:clear
無法影響其 layout 包含容器之外的 float 元素。如果依賴這個 bug 在 IE 中布局的頁面要轉(zhuǎn)到標(biāo)準(zhǔn)兼容瀏覽器中,只有全部重做。
更多相關(guān)信息查看本文 “和 CSS 規(guī)范類似的地方” 這一部分。
浮動元素旁邊的元素
當(dāng)一個塊級元素緊跟在一個左浮動元素之后時,其中的文字內(nèi)容應(yīng)該沿著浮動元素的右邊順序排列并會滑到浮動元素下方。但是如果這個塊級元素有 layout,比如由于某種原因被設(shè)置了寬度,那么這個 layout 元素就會表現(xiàn)為一個矩形,其中文字不會滑向浮動元素下方。其寬度也被錯誤計算——從浮動元素的右邊開始算起了,所以如果給它設(shè)置 width: 100%
將會導(dǎo)致顯示時這個 block 的寬度加上了浮動元素的寬度而出現(xiàn)橫向滾動條。這種表現(xiàn)就和規(guī)范中描述的相去甚遠了。
與此類似,和浮動元素相鄰的相對定位元素,它的位置偏移量應(yīng)該參照的是父元素的補白(padding)邊緣(例如,left: 0;
應(yīng)該將一個相對定位元素疊放于它前面的浮動元素之上)。在IE中,偏移量 left: value;
是從浮動元素的右邊距(margin)邊緣開始算起的,這會因浮動元素所占的寬度變化導(dǎo)致水平方向的錯位(一個解決方法是用 margin-left
代替,但是也要注意如使用百分值時會有一些怪異問題)。
根據(jù)規(guī)范所述,浮動元素應(yīng)該與其后的盒子交織在一起。而對于沒有交叉的二維空間中的矩形而言這是無法實現(xiàn)的。
可以(再次)訪問下面這個頁面:
我們可以看到跟在一個浮動元素后的 layout 元素不會顯示這個3px間隙的 bug,因為浮動元素外圍的3px硬邊無法影響一個 layout 元素的內(nèi)部內(nèi)容,所以這個硬邊將整個 layout 元素右推了3px。好比一個防護罩,layout 可以保護其內(nèi)部內(nèi)容不受影響,但是浮動元素的力量卻將整個防護罩推了開來。
更多相關(guān)信息查看本文 “和 CSS 規(guī)范類似的地方” 這一部分
列表
無論是列表本身(ol, ul
) 還是單個的列表元素(li
),擁有 layout 后都會影響列表的表現(xiàn)。不同版本 IE 的表現(xiàn)又有不同。最明顯的效果就體現(xiàn)在列表符號上(如果你的列表自定義了列表符號則不會受這個問題影響)。這些符號很可能是通過某種內(nèi)部機制附到列表元素上的(通常是附著在它們外面)。不幸的是,由于是通過“內(nèi)部機制”添加的,我們無法訪問它們也無法修正它們的錯誤表現(xiàn)。
最明顯的效果有:
- 列表獲得 layout 后,列表符號會消失或者被放置在不同的或者錯誤的位置。
有時它們又可以通過改變列表元素的邊距而重新出現(xiàn)。這看起來似乎是以下事實導(dǎo)致的結(jié)果:layout 元素會試圖裁掉超出其邊界的內(nèi)部內(nèi)容。
- 列表元素獲得 layout 之后,會有和上面一樣的問題出現(xiàn),更多參考 (extra vertical space between list items)
進一步又有一個問題就是(在有序列表中)任何具有 layout 的列表元素似乎都有自己獨立的計數(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
的錨點(anchor)時。在這種情況下,列表元素之間的空格將不會被忽略而且通常會顯示成額外的一行夾在每個 li
之間。一種避免這種豎直方向多余空白的解決方法是賦予這些錨點 layout。這樣還有一個好處就是可以讓整個錨點的矩形區(qū)域都可以響應(yīng)鼠標(biāo)點擊。
表格
table
總是有 layout 的,它總表現(xiàn)為一個已定義寬度的對象。在IE6中,table-layout: fixed
通常和一個寬度設(shè)為100%的表格相同,同時這也會帶來很多問題(一些計算方面的錯誤)。另外在IE5.5和IE6的quirks模式下還有一些別的需要注意的情況。
相對定位元素(r.p.)
注意,由于 position: relative
并不觸發(fā) hasLayout,所以很多諸如內(nèi)容消失或錯位的渲染錯誤就會因此而起。這些現(xiàn)象可能會在刷新頁面、調(diào)整窗口大小、滾動頁面、選中內(nèi)容等情況下出現(xiàn)。原因是 IE 在據(jù)這個屬性對元素做偏移處理時,卻似乎忘了發(fā)出信號讓其 layout 孩子元素進行“重繪”(而如果是一個layout元素,那么在其重繪事件的信號鏈中,這個傳給其孩子的信號是會正常發(fā)出的)。
- r.p. parent and disappearing floated child
- disappearing list-background bug
以上是一些相關(guān)問題的描述。作為經(jīng)驗之談,相對定位一個元素時最好給予其 layout。再有,我們也需要檢查擁有這種結(jié)構(gòu)的父元素是否也需要 layout 或者position: relative
亦或二者都需要,如果涉及到浮動元素這點就十分重要。
絕對定位元素(a.p.):
包含區(qū)塊,什么是包含區(qū)塊?
理解 CSS 的包含區(qū)塊概念很重要,它回答了絕對定位元素是相對哪里定位的問題:包含區(qū)塊決定了偏移起點,包含區(qū)塊定義了百分比長度的計算參考。
對于絕對定位元素,包含區(qū)塊是由其最近的定位祖先決定的。如果其祖先都沒有被定位,那么就使用初始包含區(qū)塊 html
。
通常情況下我們會用 position: relative
來設(shè)定任意包含區(qū)塊。這就是說,我們可以讓一個絕對定位元素所參考的原點和長度等不依賴于元素的排列順序,這可以滿足諸如“內(nèi)容優(yōu)先”這種可訪問性概念的需要,也可以給復(fù)雜的浮動布局帶來方便。
但是由于 layout 概念的存在,這種設(shè)計理念的效果在IE中就要打個問號了。因為在IE中絕對定位的元素是相對于其最近的 layout 定位祖先而做偏移的,而百分比的尺寸卻是參考這個 layout 定位祖先的下一個 layout 祖先計算的。注意這里的小差別,還有剛才提到 position: relative
是不會觸發(fā) hasLayout 的。
假設(shè)一個無 layout 的父元素被相對定位了——我們就得給它賦予 layout 才能使偏移量起作用:
假設(shè)一個未定位的父元素需要特定尺寸,而且頁面設(shè)計是基于百分比寬度的——我們就可以放棄這個想法了,因為瀏覽器支持不佳:
- absolutely positioned element and percentage width
濾鏡
MS專有的濾鏡屬性 filter 是只適用于 layout 元素的。有些濾鏡擴展了對象的邊界。它們會顯示出自身特有的缺陷。
對已渲染元素的重排(re-flow)
當(dāng)所有元素都已渲染完成時,如果有一個因鼠標(biāo)經(jīng)過而引起的變化產(chǎn)生(比如某個鏈接的 background
有變化),IE會對其 layout 包含區(qū)塊進行重排。有時一些元素就會因此被排到了新的位置,因為當(dāng)這個鼠標(biāo)經(jīng)過發(fā)生時,IE已經(jīng)知道了所有相關(guān)元素的寬度、偏移量等數(shù)據(jù)了。這在文檔首次載入時則不會發(fā)生,那時由于自動擴張的特性,寬度還無法確定。這種情況會導(dǎo)致在鼠標(biāo)經(jīng)過時頁面出現(xiàn)跳變。
- Jump on :hover
- quirky percentages: the reflow
這些和重排問題相關(guān)的 bug 會給百分比邊距和補白使用較多的流動布局帶來不少麻煩。
背景原點
MS專有的這個 hasLayout 還會影響背景的定位和擴展。比如,根據(jù) CSS 規(guī)范,background-position: 0 0
應(yīng)該指元素的“補白邊緣(padding edge)”。而在 IE/Win 下,如果 hasLayout = false
則指的是“邊框邊緣(border edge)”,當(dāng) hasLayout=true
時指的才是補白邊緣:
- Background, Border, hasLayout
邊距重疊
hasLayout
會影響一個盒子和其子孫的邊距重疊。根據(jù)規(guī)范,一個盒子如果沒有上補白和上邊框,那么它的上邊距應(yīng)該和其文檔流中的第一個孩子元素的上邊距重疊:
- Collapsing Margins
- Uncollapsing Margins
在 IE/Win 中如果這個盒子有 layout 那么這種現(xiàn)象就不會發(fā)生了:似乎擁有 layout 會阻止其孩子的邊距伸出包含容器之外。此外當(dāng) hasLayout = true
時,不論包含容器還是孩子元素,都會有邊距計算錯誤的問題出現(xiàn)。
- Margin collapsing and hasLayout
塊級別的鏈接
hasLayout 會影響一個塊級別鏈接的鼠標(biāo)響應(yīng)區(qū)域(可點擊區(qū)域)。通常 hasLayout = false 時只有文字覆蓋區(qū)域才能響應(yīng)。而 hasLayout = true 則整個塊狀區(qū)域都可響應(yīng)。添加了 onclick/onmouseover 等事件的任意塊級元素也有同樣的現(xiàn)象。
- Block anchors and hasLayout
在頁面內(nèi)使用鍵盤瀏覽:探索中
當(dāng)使用 tab 在頁面中瀏覽時,如果進入了一個頁內(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)成)。
堆疊,分層和 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)驗之談:調(diào)試的時候,兩種情況都要考慮到。我們可能會有規(guī)律地在下拉菜單或者類似的復(fù)雜菜單中看到相關(guān)問題,因為它們往往牽涉到堆疊,定位和浮動等諸多令人頭疼的問題。給那些 z-index 定位的元素 layout 是一種可能的修正方法,不過也不限于此,這里只是提醒一下。
混亂的 contenteditable
如果給一個 HTML 標(biāo)簽設(shè)定 contenteditable=true
屬性,比如body contenteditable=true>
,將會允許對該元素以及其 layout 子元素進行實時的編輯、拖動改變尺寸等操作。你可以把這屬性用在浮動元素或者一個有序列表中的 layout 列表元素上看看效果。
為了對元素進行操作(編輯它們),“contenteditable”和“hasLayout”為那些 hasLayout
返回 true
的元素引入了一套單獨的堆疊順序。
Editing Platform 繼承了 layout 概念,對 layout 的誤解多是因 contenteditable 而起即可作為證明(那些某種程度上集成了IE編輯引擎的應(yīng)用軟件多暗含著對layout概念的某種強制向后兼容性)。
和 CSS 規(guī)范類似的地方
你的 MSIE 頁面在別的瀏覽器中一團糟?我們可沒必要讓這種事情發(fā)生。如果使用恰當(dāng),任何好的瀏覽器都能擺平 MSIE 的頁面——只要你使用一些正確的 CSS。
利用 hasLayout
和“新的塊級格式內(nèi)容”之間的細微相似之處,我們可以有幾種方法在標(biāo)準(zhǔn)兼容瀏覽器中重新實現(xiàn) hasLayout
的“包含浮動元素”效果,和一些“浮動元素旁邊的元素”所特有的效果。
- Reverse engineering series
- Simulations
Quirks 模式
某些 doctype,或者 xml>
聲明,在 IE6 中會觸發(fā)“quirks模式”或曰向后兼容模式。在這種模式下,IE6 就像 IE5.5,并且和它老弟擁有一樣的bug,一樣的問題和一樣的行為。
而對于IE7,xml>
聲明不會再改變渲染模式了;要觸發(fā) quirks 模式,我們不得不插入一個注釋才行。(IE7 的 quirks 模式和 IE6 的 quirks 模式是否一樣還有待驗證)
?xml version="1.0" encoding="utf-8"?>
!-- ... 讓 IE7 運行在 quirks 模式 -->
!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
Layout — 結(jié)論
整個 layout 概念和一些基本 CSS 概念是不兼容的,即包含,排列,浮動,定位和層疊等。
由于頁面中元素或有或沒有 layout,會導(dǎo)致 IE/Win 的行為和 CSS 規(guī)范相違背。
擁有 layout — 另外一個引擎?
IE 的對象模型看起來是文檔模型和他們傳統(tǒng)的應(yīng)用程序模型的糅合。我之所以提到這點是因為它對于理解IE如何渲染頁面很重要。而從文檔模型切換到應(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日。