譯者註:一篇很好的文章,很久以前在blog上就推薦過,這兩天斷斷續續花了點時間翻譯了一下,推薦讀讀。英文原文在此。
文中所有的 layout 這個單詞都未作翻譯,一來本身這個單詞意思就比較多,翻成啥都覺得彆扭,二來它也是專有的屬性,所以就意會一下吧。水平有限,很多地方都是模模糊糊地意譯,發現錯誤歡迎留言指出。
引用一段來自Dean Edwards的評價:
I recommend that every CSS designer and DOM scripter read this. Understanding “layout” gives a huge insight into lots of other IE bugs and idiosyncrasies.
(Dean Edwards)
介紹
Internet Explorer 中有很多奇怪的渲染問題可以通過賦予其“layout”得到解決。John Gallant 和 Holly Bergevin 把這些問題歸類為“尺寸bug(dimensional bugs)”,意思是這些 bug 可以通過賦予相應元素某個寬度或高度解決。這便引出關於“layout”的一個問題:為什麼它會改變元素的渲染特性,為什麼它會影響到元素之間的關係?這個問題問得很好,但卻很難回答。在這篇文章中,我們專註於這個複雜問題會有那些方面的表現,某一方面的具體討論和範例請參考文中給出的相關連結。
hasLayout — 定義
“Layout”是一個 IE/Win 的私人概念,它決定了一個元素如何顯示以及約束其包含的內容、如何與其他元素互動和建立聯絡、如何響應和傳遞應用程式事件/使用者事件等,這有點類似於一個表單的概念。
微軟的開發人員們認為盒狀元素(box-type elements)應該具有一個“屬性(property)”(這是物件導向編程中的一個概念),於是他們便使用了 layout
, 也就是 hasLayout
。
hasLayout
其實既不是一個屬性更不是一個行為,而是 IE 這個渲染引擎代代繼承一貫擁有的一個渲染概念,在這個概念下渲染的元素將具有一種特性。
實際上這種渲染特性在有些 HTML 元素中與身俱來,而在另外一些元素中也可以通過一些 CSS 屬性將其觸發為 true
,且一旦觸發將無法復原轉。
術語
當我們說一個元素“擁有layout”或“得到layout”,或者說一個元素“has layout” 的時候,我們的意思是指它的微軟專有屬性 hasLayout
被設為了 true
。一個“layout元素”可以是一個預設就擁有 layout 的元素或者是一個通過設定某些 CSS 屬性得到 layout 的元素。
而“無layout元素”,是指 hasLayout 未被觸發的元素,比如一個未設定寬高尺寸的乾淨 div 元素就可以做為一個 “無layout祖先”。
給一個預設沒有 layout 的元素賦予 layout 的方法包括設定可觸發 hasLayout = true
的 CSS 屬性。參考預設 layout 元素以及這些屬性列表。沒有辦法設定 hasLayout = false
, 除非把一開始那些觸發 hasLayout = true
的 CSS 屬性去除。
問題種種
hasLayout
的問題不管新手還是老手,不管設計師或者程式員可能都遇到過。具有 layout 的元素通常有著不同尋常而且難以預料的的顯示效果,而且有時甚至會牽連到他們的孩子項目。
一個元素是否具有“layout”可能會引發如下的一些問題:
- IE 很多常見的浮動 bug 。
- 元素本身對一些基本屬性的異常處理問題。
- 容器和其子孫之間的邊距重疊(margin collapsing)問題。
- 使用列表時遇到的諸多問題。
- 背景映像的定位偏差問題。
- 使用指令碼時遇到的瀏覽器之間處理不一致的問題。
上面的列表只是列出一個大概,也不完善。下面的文章將儘可能詳細徹底的描述有無“layout”所帶來的各種問題。
Layout 從何而來
不同於標準屬性,也不像某些瀏覽器的私人 CSS 屬性,layout 無法通過某一個 CSS 聲明直接設定 。也就是說沒有“layout屬性”這麼一個東西,元素要麼本身自動擁有 layout,要麼藉助一些 CSS 聲明悄悄地獲得 layout。
預設layout元素
下列元素應該是預設具有 layout 的:
,
,
,
, |
, , , , <embed>, <object>, </CODE> <LI><CODE><marquee></CODE> </LI></UL><H5 id=prop>屬性</H5><P> 下列 CSS 屬性和取值將會讓一個元素獲得 layout:</P><DL><DT><CODE>position: absolute</CODE> <DD>絕對位置元素的包含區塊(containing block)就會經常在這一方面出問題。 <DT><CODE>float: left|right</CODE> <DD>由於 layout 元素的特性,浮動模型會有很多怪異的表現。 <DT><CODE>display: inline-block</CODE> <DD>當一個內聯層級的元素需要 layout 的時候往往就要用到它,這也可能也是這個 CSS 屬性的唯一效果——讓某個元素擁有 layout。“inline-block行為”在IE中是可以實現的,但是非常與眾不同: IE/Win: inline-block and hasLayout 。 <DT><CODE>width: 任意值</CODE> <DD>很多人遇到 layout 相關問題發生時,一般都會先嘗試用這個來修複。 <DT><CODE>height: 任意值</CODE> <DD>height: 1% 就在 Holly Hack 中用到。 <DT><CODE>zoom: 任意值</CODE> (MSDN) <DD>MS專有屬性,無法通過校正。 不過 <CODE>zoom: 1</CODE> 可以臨時用做調試。 <DT><CODE>writing-mode: tb-rl</CODE> (MSDN) <DD>MS專有屬性,無法通過校正。 </DD></DL><P> 在 IE7 中,overflow 也變成了一個 layout 觸發器:</P><DL><DT><CODE>overflow: hidden|scroll|auto</CODE> <DD>這個屬性在之前版本 IE 中沒有觸發 layout 的功能。 <DT><CODE>overflow-x|-y: hidden|scroll|auto</CODE> <DD>overflow-x 和 overflow-y 是 CSS3 盒模型中的屬性,尚未得到瀏覽器的廣泛支援。他們在之前版本IE中沒有觸發 layout 的功能。 </DD></DL><P> 另外 IE7 的熒幕上又新添了幾個 haslayout 的演員,如果只從 hasLayout 這個方面考慮,min/max 和 width/height 的表現類似,position 的 fixed 和 absolute 也是一模一樣。</P><DL><DT><CODE>position: fixed</CODE> <DD>./. <DT><CODE>min-width: 任意值</CODE> <DD>就算設為0也可以讓該元素獲得 layout。 <DT><CODE>max-width: 除 none 之外的任意值</CODE> <DD>./. <DT><CODE>min-height: 任意值</CODE> <DD>即使設為0也可以讓該元素的 haslayout=true <DT><CODE>max-height: 除 none 之外的任意值</CODE> <DD>./. </DD></DL><P> 以上結論藉助 IE Developer Toobar 以及預先測試得出。</P><H5 id=inline>有關內聯層級元素</H5><P> 對於內嵌元素(可以是預設即為內聯的比如 <CODE>span</CODE> 元素,也可以是 <CODE>display: inline</CODE> 的元素)</P><UL><LI><CODE>width</CODE> 和 <CODE>height</CODE> 只在 IE5.x 下和 IE6 的 quirks 模式下觸發 <CODE>hasLayout</CODE> 。因為在 IE6 中,如果瀏覽器運行於標準相容模式下,內嵌元素會忽略 width 或 height 屬性,所以設定 width 或 height 不能在此種情況下令該元素具有 layout。 <LI><CODE>zoom</CODE> 總是可以觸發 <CODE>hasLayout</CODE>,但是在 IE5.0 中不支援。 </LI></UL><P> 具有“layout” 的元素如果同時也 <CODE>display: inline</CODE> ,那麼它的行為就和標準中所說的 inline-block 很類似了:在段落中和普通文字一樣在水平方向和連續排列,受 vertical-align 影響,並且大小可以根據內容自適應調整。這也可以解釋為什麼單單在 IE/Win 中內嵌元素可以包含區塊層級元素而少出問題,因為在別的瀏覽器中 <CODE>display: inline</CODE> 就是內聯,不像 IE/Win 一旦內嵌元素擁有 layout 還會變成 inline-block。</P><H5 id=haslayoutprop>指令碼屬性 hasLayout</H5><P> 我們這裡稱 hasLayout 為“指令碼屬性”是為了和我們熟知的 CSS 屬性相區別。</P><P> 注意一旦一個元素擁有了 layout,就沒有辦法再將其設成 <CODE>hasLayout = False</CODE> 了。</P><P> hasLayout-property 可以用來檢測一個元素是否擁有 layout:舉個例子,如果它的 <CODE>id</CODE> 是“eid”,那麼只要在 IE5.5+ 的地址欄裡輸入 <CODE class=c1>javascript: alert(eid.currentStyle.hasLayout)</CODE> 即可檢測它的狀態。</P><P> IE的 Developer Toolbar 可以即時檢查一個元素的當前樣式;如果 <CODE>hasLayout</CODE> 是 true ,那麼它的值顯示為 “-1”。 我們可以通過即時修改一個元素的屬性將“zoom(css)”設定為“1”來觸發 <CODE>hasLayout</CODE> 以便調試。</P><P> 另外一個需要注意的是“layout”會影響指令碼編程。如果一個元素沒有“layout”,那麼<CODE>clientWidth</CODE>/<CODE>clientHeight</CODE> 總是返回0。這會讓一些指令碼新手感到困惑,而且這和 Mozilla 瀏覽器的處理方式也不一樣。不過我們可以利用這一點在 IE5.0 中檢測“layout”:如果 <CODE>clientWidth</CODE> 是零那麼這個元素就沒有 layout。</P><H4 id=hack>CSS hacks</H4><P> 下面用於觸發 <CODE>haslayout</CODE> 的 hack 已經經過 IE6 及以下版本測試。今後版本的IE有可能會對此做不同處理。如果新版本瀏覽器發布我們會重新整理這部分內容。</P><P> John Gallant 和 Holly Bergevin 在2003年發布的 Holly hack :</P><PRE class=code>/* \*/* html .gainlayout { height: 1%; }/* */</PRE><UL><LI>可以讓 IE5+ 的任意元素獲得 layout,除了標準相容模式 IE6 中的內嵌元素。 <LI>一般都很有效,除了在某些極少情況下,需要用 height:0 或者 1px 更好一些。 <LI>和 <CODE>overflow: hidden</CODE> 不相容,除非在 IE6 的標註相容模式下(因為這時如果父元素沒有定高,那麼<CODE>height: 1%</CODE> 會被變回 <CODE>height: auto</CODE>)。 </LI></UL><P> 或者我們可以用 underscore hack:</P><PRE class=code>.gainlayout { _height: 0; }</PRE><P> 另外,更具有向後相容性的方法是使用 條件注釋(conditional comments):</P><PRE class=code><!--[if lte IE 6]><style>.gainlayout { height: 1px; }</style><![endif]--></PRE><P> 在條件注釋中連結一個專門對 IE/Win 做修正的外部樣式表檔案,也不失為一個安全有效好方法:</P><PRE class=code><link rel="stylesheet" href="allbrowsers.css" type="text/css" /><!--[if lte IE 6]><link rel="stylesheet" href="iefix.css" type="text/css" /><![endif]--></PRE><P> 我們更傾向於使用 <CODE>height: 0</CODE> 和 <CODE>1px</CODE> —— 並主張始終使用 <CODE>height</CODE> 除非它和別的什麼東西衝突 (<CODE>overflow: hidden</CODE>)。對於取值,我們則傾向於避免 <CODE>1%</CODE> ,因為它可能會(雖然很少)引起一些問題。</P><P> 一個需要注意的情況是如果我們希望一個元素保持內聯,那麼就不能使用 <CODE>height</CODE> 了,這時可以用 <CODE>display: inline-block</CODE> 。我們只在早期調試階段用 <CODE>zoom: 1</CODE> 來避免一些渲染錯誤。</P><P> 我們曾看過一些把 Holly hack 真的當作 holy(神聖的) hack 盲目使用的情況,比如對浮動元素使用或者對已經具有特定寬度的元素也使用這個 hack。要記住這個 hack 的目的不是要給某個元素加一個高度,而只是要觸發 <CODE>hasLayout = True</CODE> 而已。</P><P> 不要給所有元素設定 layout:<CODE>* {_height: 1px;}</CODE>。所謂過猶不及,獲得 layout 不等於獲得靈丹妙藥,它只是用來改變渲染模式。</P><H5 id=hackmanagement>Hack整理</H5><P> 但是瀏覽器總是會變的,我們需要面對很多問題,比如一些依賴 IE6 的 bug 所做的 hack 會在 IE7 或更高版本的新瀏覽器中因 bug 修複而失效(甚至有害)的問題;比如新版本瀏覽器中類似的布局 bug 依然存在但用於 hack 的過濾器比如 <CODE>* html</CODE> 卻不能正常工作的問題。這種情況下,MS專有屬性 <CODE>zoom</CODE> 就可以考慮使用了。</P><PRE class=code><!--[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]--></PRE><UL><LI><CODE>zoom: 1;</CODE> 可以讓 IE5.5+ 的任何元素(包括內嵌元素)獲得 layout,但是在 IE5.0 中無效。 <LI>沒有其他附帶效果(內嵌元素會變成 inline-block,這個當然)。 <LI>如果需要通過驗證,應該用條件注釋將 <CODE>zoom</CODE> 隱藏起來。 </LI></UL><P> 其實當我們考慮到“向後相容”時是很自相矛盾的,我們強烈建議頁面設計者回過頭看一下自己頁面中用的到的明顯的或是不明顯的“hacks”,並用條件注釋針對不同瀏覽器重新處理以保萬無一失。</P><H4 id=iemac>關於IE Mac 的小問題</H4><P> IE Mac 和 windows 下的 IE 是完全不同的兩個東西,它們各自擁有自己的渲染引擎,IE Mac 就全然不知“hasLayout”(或contenteditable)所謂何物。相比之下 IE Mac 的渲染引擎要更標準相容一點,比如 <CODE>height</CODE> 就是被當作 <CODE>height</CODE> 處理,沒有別的效果。因此針對“hasLayout”的 hacks 和別的解決方案(特別是通過使用 <CODE>height</CODE> 或 <CODE>width</CODE> 屬性的)往往對 IE Mac 來說是有害的,所以需要對其隱藏。更多的關於 IE Mac 相關的問題可以在 IE Mac, bugs and oddities pages 找到。</P><H4 id=docu>MSDN 文檔</H4><P> MSDN 中涉及到 hasLayout 這個 MS 屬性的地方寥寥無幾,而具體解釋 layout 和 IE 渲染模型之間關係的則少之又少。</P><P> 在IE4的時候,除了未經絕對位置也未指定寬高的內嵌元素,幾乎所有元素都有某種 layout(MSDN)。在這種早期的layout概念中,像 <CODE>border, margin, padding</CODE> 這些屬性被稱作“layout屬性”,它們是不能應用到一個簡單的內嵌元素上的。換句話說,“擁有layout”就可以粗略理解成:“可以擁有這幾個屬性”。</P><P> MSDN 上仍然使用 layout 屬性這種說法, 只是含義變了,它們和擁有 layout 的元素已經沒有什麼關係了。在 IE5.5 中方才引入了 MS 的這個專有屬性 <CODE>hasLayout</CODE>,也只是某種內部的標誌位而已。</P><P> 在 IE5.5 中,MSHTML Editing Platform(即可以通過設定<CODE><body contenteditable=true></CODE>來允許使用者即時編輯、拖動 layout 元素以及調整其尺寸等)的文檔中描述了三個和 layout 相關的重要特性:</P><BLOCKQUOTE title="來自MSDN規範: Editing Platform" cite=http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnmshtml/html/mshtmleditplatf.asp><P> 如果一個 layout 元素中有內容,內容的排版布局將由它的邊界矩形框決定。</P><P> 擁有 layout 的意思基本上就是表示某元素是一個矩形。</P><P> 從內部來說,擁有 layout 意思就是一個元素將自己負責繪製其內部內容。</P></BLOCKQUOTE><P class=blockquotesource>(Editing Platform)</P><P> 和 layout 自身相關的內部工作機制直到2005年8月才有相應文檔描述,當時由於 The Web Standards Project 和微軟的特別工作小組的原因,Markus Mielke [MSFT] 開啟了深入討論的大門:</P><BLOCKQUOTE title="來自MSDN規範:Filters and Transitions" cite=http://msdn.microsoft.com/library/default.asp?url=/library/en-us/IETechCol/cols/dnexpie/expie20050831.asp><P> 一般來說,在 Internet Explorer 的 DHTML 引擎中,元素是不對自己的位置安排負責的。雖然一個 div 或者一個 p 元素都在源碼中有一個位置,在文檔流有一個位置,但是它們的內容卻是由它們最近的一個 layout 祖先(經常是 body)控制安排的。這些元素依賴它們祖先的 layout 來為他們處理諸如決定大小尺寸和測量資訊等諸多繁重的工作。</P></BLOCKQUOTE><P class=blockquotesource>(HasLayout概述)</P><H4 id=interpr>分析</H4><P> 我們的分析試圖解釋在已知案例下發生了什麼事情,這種分析也應該可以作為未知案例下的指導。但我們這種試圖利用種種測試案例投石探路的黑箱測試方法,是註定無法消除黑箱的神秘感的——我們無法回答“為什麼”的問題。我們只能去嘗試瞭解整個“hasLayout”模式的工作架構,以及它會怎樣影響網頁文檔的渲染。因此,最終我們只能提供一些<EM>指導方針</EM>(而且只能是指導方針,而不是絕對的解決方案)。</P><P> 我們認為他們所指的是一個小表單。一個 layout 元素的內部內容是完全獨立的,而且也無法影響其邊界外的任何內容。</P><P> 而 MS 屬性 layout 只是某種標誌位:一旦它被設定,這個元素就會擁有其特殊的 layout“特性”,這包括體現在其自身以及其非 layout 孩子身上的浮動、清除浮動、層疊、計數等等諸多方面的特殊效能。</P><P> 這種獨立性也許正可以解釋為什麼 layout 元素通常比較穩定,而且它們可以讓某些 bug 消失。這種情況的代價有二,一是偏離了標準,二是它沒有考慮到今後可能因此出現的 bug 和問題。</P><P> MS 的“頁面”模式,從符號學角度考慮,可以看做是由很多互不相關的小的區塊構成,而 HTML 和 W3C 的模式則認為“頁面”模式應該是敘述完備的,故事性的相關資訊區塊構成的。</P><H4 id=rev>各種情況的詳細說明</H4><H5 id=clear>清除浮動和自動擴充適應高度</H5><P> 浮動元素會被 layou 元素自動包含。這是很多新手經常遇到的問題:在 IE 下完成的頁面到了標準相容瀏覽器下所有未清除的浮動元素都伸出了其包含容器之外。</P><UL><LI>Containing Floats <LI>how to clear floats without structural markup </LI></UL><P> 相反的情況:如果確實需要一個浮動元素伸出其包含容器,也就是自動包含不是想要的效果時,該怎麼辦?你很可能也會遇到這種頭疼的問題,下面的深入討論就是一個例子:</P><UL><LI>acidic float tests </LI></UL><P> 在IE中,一個浮動元素總是“隸屬於”它的 layout 包含容器。而後面的元素會受這個 layout 包含容器影響而不是這個浮動元素影響。</P><P> 這個特性和IE6的那個自動擴充以適應內部內容寬度的特性,都可以看成是受這個規則影響的:“由它的邊界矩形框決定”。</P><P> 更糟的問題:<CODE>clear</CODE> 無法影響其 layout 包含容器之外的 float 元素。如果依賴這個 bug 在 IE 中布局的頁面要轉到標準相容瀏覽器中,只有全部重做。</P><P> 更多相關資訊查看本文 “和 CSS 規範類似的地方” 這一部分。</P><H5 id=nextfloat>浮動元素旁邊的元素</H5><P> 當一個區塊層級元素緊跟在一個左浮動元素之後時,其中的文字內容應該沿著浮動元素的右邊順序排列並會滑到浮動元素下方。但是如果這個區塊層級元素有 layout,比如由於某種原因被設定了寬度,那麼這個 layout 元素就會表現為一個矩形,其中文字不會滑向浮動元素下方。其寬度也被錯誤計算——從浮動元素的右邊開始算起了,所以如果給它設定 <CODE>width: 100%</CODE> 將會導致顯示時這個 block 的寬度加上了浮動元素的寬度而出現橫向捲軸。這種表現就和規範中描述的相去甚遠了。</P><P> 與此類似,和浮動元素相鄰的相對定位元素,它的位置位移量應該參照的是父元素的補白(padding)邊緣(例如,<CODE>left: 0;</CODE> 應該將一個相對定位元素疊放於它前面的浮動元素之上)。在IE中,位移量 <CODE>left: value;</CODE> 是從浮動元素的右邊距(margin)邊緣開始算起的,這會因浮動元素所佔的寬度變化導致水平方向的錯位(一個解決方案是用 <CODE>margin-left</CODE> 代替,但是也要注意如使用百分值時會有一些怪異問題)。</P><P> 根據規範所述,浮動元素應該與其後的盒子<EM>交織</EM>在一起。而對於沒有交叉的二維空間中的矩形而言這是無法實現的。</P><P> 可以(再次)訪問下面這個頁面:</P><UL><LI>three pixel text-jog </LI></UL><P> 我們可以看到跟在一個浮動元素後的 layout 元素不會顯示這個3px間隙的 bug,因為浮動元素外圍的3px硬邊無法影響一個 layout 元素的內部內容,所以這個硬邊將整個 layout 元素右推了3px。好比一個防護罩,layout 可以保護其內部內容不受影響,但是浮動元素的力量卻將整個防護罩推了開來。</P><P> 更多相關資訊查看本文 “和 CSS 規範類似的地方” 這一部分</P><H5 id=list>列表</H5><P> 無論是列表本身(<CODE>ol, ul</CODE>) 還是單個的列表元素(<CODE>li</CODE>),擁有 layout 後都會影響列表的表現。不同版本 IE 的表現又有不同。最明顯的效果就體現在列表符號上(如果你的列表自訂了列表符號則不會受這個問題影響)。這些符號很可能是通過某種內部機制附到列表元素上的(通常是附著在它們外面)。不幸的是,由於是通過“內部機制”添加的,我們無法訪問它們也無法修正它們的錯誤表現。</P><P> 最明顯的效果有:</P><UL><LI>列表獲得 layout 後,列表符號會消失或者被放置在不同的或者錯誤的位置。 </LI></UL><P> 有時它們又可以通過改變列表元素的邊距而重新出現。這看起來似乎是以下事實導致的結果:layout 元素會試圖裁掉超出其邊界的內部內容。</P><UL><LI>列表元素獲得 layout 之後,會有和上面一樣的問題出現,更多參考 (extra vertical space between list items) </LI></UL><P> 進一步又有一個問題就是(在有序列表中)任何具有 layout 的列表元素似乎都有自己獨立的計數器。比如我們有一個含五個列表元素的有序列表,只有第三個列表元素有 layout。我們會看到這樣:</P><P> 1… 2… 1… 4… 5…</P><P> 此外,如果一個有 layout 的列表元素跨行顯示時,列表符號會底部對齊(而不是按照預料的頂部對齊)。</P><P> 以上某些問題還是無法解決的,所以如果需要列表符號的時候最好避免讓列表和列表元素獲得 layout。如果需要限定尺寸,最好給別的元素設定尺寸,比如給整個列表外面套一個元素並設定它的寬度,又或者比如給每個列表元素中的內容設定高度等等。</P><P> 另一個IE中列表的常見問題出現在當某個 <CODE>li</CODE> 中的內容是一個 <CODE>display: block</CODE> 的錨點(anchor)時。在這種情況下,列表元素之間的空格將不會被忽略而且通常會顯示成額外的一行夾在每個 <CODE>li</CODE> 之間。一種避免這種豎直方向多餘空白的解決方案是賦予這些錨點 layout。這樣還有一個好處就是可以讓整個錨點的矩形地區都可以響應滑鼠點擊。</P><H5 id=tab>表格</H5><P> <CODE>table</CODE> 總是有 layout 的,它總表現為一個已定義寬度的對象。在IE6中,<CODE>table-layout: fixed</CODE> 通常和一個寬度設為100%的表格相同,同時這也會帶來很多問題(一些計算方面的錯誤)。另外在IE5.5和IE6的quirks模式下還有一些別的需要注意的情況。</P><H5 id=rp>相對定位元素(r.p.)</H5><P> 注意,由於 <CODE>position: relative</CODE> 並不觸發 hasLayout,所以很多諸如內容消失或錯位的渲染錯誤就會因此而起。這些現象可能會在重新整理頁面、調整視窗大小、滾動頁面、選中內容等情況下出現。原因是 IE 在據這個屬性對元素做位移處理時,卻似乎忘了發出訊號讓其 layout 孩子項目進行“重繪”(而如果是一個layout元素,那麼在其重繪事件的訊號鏈中,這個傳給其孩子的訊號是會正常發出的)。</P><UL><LI>r.p. parent and disappearing floated child <LI>disappearing list-background bug </LI></UL><P> 以上是一些相關問題的描述。作為經驗之談,相對定位一個元素時最好給予其 layout。再有,我們也需要檢查擁有這種結構的父元素是否也需要 layout 或者<CODE>position: relative</CODE>亦或二者都需要,如果涉及到浮動元素這點就十分重要。</P><H5 id=cb>絕對位置元素(a.p.):<BR><SPAN>包含區塊,什麼是包含區塊?</SPAN></H5><P> 理解 CSS 的包含區塊概念很重要,它回答了絕對位置元素是相對哪裡定位的問題:包含區塊決定了位移起點,包含區塊定義了百分比長度的計算參考。</P><P> 對於絕對位置元素,包含區塊是由其最近的定位祖先決定的。如果其祖先都沒有被定位,那麼就使用初始包含區塊 <CODE>html</CODE>。</P><P> 通常情況下我們會用 <CODE>position: relative</CODE> 來設定任意包含區塊。這就是說,我們可以讓一個絕對位置元素所參考的原點和長度等不依賴於元素的排列順序,這可以滿足諸如“內容優先”這種可訪問性概念的需要,也可以給複雜的浮動布局帶來方便。</P><P> 但是由於 layout 概念的存在,這種設計理念的效果在IE中就要打個問號了。因為在IE中絕對位置的元素是相對於其最近的 <EM>layout 定位</EM>祖先而做位移的,而百分比的尺寸卻是參考這個 layout 定位祖先的下一個 <EM>layout</EM> 祖先計算的。注意這裡的小差別,還有剛才提到 <CODE>position: relative</CODE> 是不會觸發 hasLayout 的。</P><P> 假設一個無 layout 的父元素被相對定位了——我們就得給它賦予 layout 才能使位移量起作用:</P><UL><LI>Absolutely Buggy II </LI></UL><P> 假設一個未定位的父元素需要特定尺寸,而且頁面設計是基於百分比寬度的——我們就可以放棄這個想法了,因為瀏覽器支援不佳:</P><UL><LI>absolutely positioned element and percentage width </LI></UL><H5 id=filter>濾鏡</H5><P> MS專有的濾鏡屬性 filter 是只適用於 layout 元素的。有些濾鏡擴充了對象的邊界。它們會顯示出自身特有的缺陷。</P><H5 id=reflow>對已渲染元素的重排(re-flow)</H5><P> 當所有元素都已渲染完成時,如果有一個因滑鼠經過而引起的變化產生(比如某個連結的 <CODE>background</CODE> 有變化),IE會對其 layout 包含區塊進行重排。有時一些元素就會因此被排到了新的位置,因為當這個滑鼠經過發生時,IE已經知道了所有相關元素的寬度、位移量等資料了。這在文檔首次載入時則不會發生,那時由於自動擴張的特性,寬度還無法確定。這種情況會導致在滑鼠經過時頁面出現跳變。</P><UL><LI>Jump on :hover <LI>quirky percentages: the reflow </LI></UL><P> 這些和重排問題相關的 bug 會給百分比邊距和補白使用較多的流動布局帶來不少麻煩。</P><H5 id=bgorigin>背景原點</H5><P> MS專有的這個 hasLayout 還會影響背景的定位和擴充。比如,根據 CSS 規範,<CODE>background-position: 0 0</CODE> 應該指元素的“補白邊緣(padding edge)”。而在 IE/Win 下,如果 <CODE>hasLayout = false</CODE> 則指的是“邊框邊緣(border edge)”,當 <CODE>hasLayout=true</CODE> 時指的才是補白邊緣:</P><UL><LI>Background, Border, hasLayout </LI></UL><H5 id=uncollapse>邊距重疊</H5><P> <CODE>hasLayout</CODE> 會影響一個盒子和其子孫的邊距重疊。根據規範,一個盒子如果沒有上補白和上邊框,那麼它的上邊距應該和其文檔流中的第一個孩子項目的上邊距重疊:</P><UL><LI>Collapsing Margins <LI>Uncollapsing Margins </LI></UL><P> 在 IE/Win 中如果這個盒子有 layout 那麼這種現象就不會發生了:似乎擁有 layout 會阻止其孩子的邊距伸出包含容器之外。此外當 <CODE>hasLayout = true </CODE>時,不論包含容器還是孩子項目,都會有邊距計算錯誤的問題出現。</P><UL><LI>Margin collapsing and hasLayout </LI></UL><H5 id=link>塊層級的連結</H5><P> hasLayout 會影響一個塊層級連結的滑鼠響應地區(可點擊地區)。通常 hasLayout = false 時只有文字覆蓋地區才能響應。而 hasLayout = true 則整個塊狀地區都可響應。添加了 onclick/onmouseover 等事件的任意區塊層級元素也有同樣的現象。</P><UL><LI>Block anchors and hasLayout </LI></UL><H5 id=inpage>在頁面內使用鍵盤瀏覽:探索中</H5><P> 當使用 tab 在頁面中瀏覽時,如果進入了一個頁內連結(in-page link),那麼接下來再按的 <KBD>tab</KBD> 鍵就不會正常繼續了:</P><UL><LI>hasLayout Property Characterizes IE6 Bug <LI>Keyboard Navigation and Internet Explorer </LI></UL><P> <KBD>tab</KBD> 鍵會把使用者帶到(這通常是錯誤的)其最近的 layout 祖先中的第一個目標(如果這個祖先是由 <CODE>table</CODE>, <CODE>div</CODE>, <CODE>span</CODE> 或某些別的標籤構成)。</P><H4 id=stack>堆疊,分層和 layout </H4><P> IE/Win 中似乎有兩種分層和堆疊順序:</P><UL><LI>一種是(偽)試圖採用CSS的模式:Effect of z-index value to RP and AP blocks <LI>還有一種是由“hasLayout”及其孿生兄弟“contenteditable”的行為產生的堆疊順序。正如在上面相對定位的例子中展現的那樣,在 layout 影響下的堆疊現象就好像 Harry Houdini (譯者註:魔術師,以紙牌魔術成名)的拿手戲法兒一樣。 </LI></UL><P> 兩種堆疊模式雖互不相容,但卻共存於IE的渲染引擎中。經驗之談:調試的時候,兩種情況都要考慮到。我們可能會有規律地在下拉式功能表或者類似的複雜菜單中看到相關問題,因為它們往往牽涉到堆疊,定位和浮動等諸多令人頭疼的問題。給那些 z-index 定位的元素 layout 是一種可能的修正方法,不過也不限於此,這裡只是提醒一下。</P><H4 id=contenteditable>混亂的 contenteditable</H4><P> 如果給一個 HTML 標籤設定 <CODE>contenteditable=true</CODE> 屬性,比如<CODE><body contenteditable=true></CODE>,將會允許對該元素以及其 layout 子項目進行即時的編輯、拖動改變尺寸等操作。你可以把這屬性用在浮動元素或者一個有序列表中的 layout 列表元素上看看效果。</P><P> 為了對元素進行操作(編輯它們),“contenteditable”和“hasLayout”為那些 <CODE>hasLayout</CODE> 返回 <CODE>true</CODE> 的元素引入了一套單獨的堆疊順序。</P><P> Editing Platform 繼承了 layout 概念,對 layout 的誤解多是因 contenteditable 而起即可作為證明(那些某種程度上整合了IE編輯引擎的應用軟體多暗含著對layout概念的某種強制向後相容性)。</P><UL><LI>More on contenteditable </LI></UL><H4 id=engineer>和 CSS 規範類似的地方</H4><P> 你的 MSIE 頁面在別的瀏覽器中一團糟?我們可沒必要讓這種事情發生。如果使用恰當,任何好的瀏覽器都能擺平 MSIE 的頁面——只要你使用一些正確的 CSS。</P><P> 利用 <CODE>hasLayout</CODE> 和“新的塊級格式內容”之間的<EM>細微</EM>相似之處,我們可以有幾種方法在標準相容瀏覽器中重新實現 <CODE>hasLayout</CODE> 的“包含浮動元素”效果,和一些“浮動元素旁邊的元素”所特有的效果。</P><UL><LI>Reverse engineering series <LI>Simulations </LI></UL><H4 id=quirk>Quirks 模式</H4><P> 某些 doctype,或者 <CODE><xml></CODE> 聲明,在 IE6 中會觸發“quirks模式”或曰向後相容模式。在這種模式下,IE6 就像 IE5.5,並且和它老弟擁有一樣的bug,一樣的問題和一樣的行為。</P><P> 而對於IE7,<CODE><xml></CODE> 聲明不會再改變渲染模式了;要觸發 quirks 模式,我們不得不插入一個注釋才行。(IE7 的 quirks 模式和 IE6 的 quirks 模式是否一樣還有待驗證)</P><PRE class=code><?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"></PRE><H4 id=conclusion>Layout — 結論</H4><P> 整個 layout 概念和一些基本 CSS 概念是不相容的,即包含,排列,浮動,定位和層疊等。</P><P> 由於頁面中元素或有或沒有 layout,會導致 IE/Win 的行為和 CSS 規範相違背。</P><H4 id=bottomline>擁有 layout — 另外一個引擎?</H4><BLOCKQUOTE title="Dean Edward 關於IE7的一些說明" cite=http://dean.edwards.name/IE7/notes/#layout><P> IE 的物件模型看起來是文檔模型和他們傳統的應用程式模型的糅合。我之所以提到這點是因為它對於理解IE如何渲染頁面很重要。而從文檔模型切換到應用程式模型的開關就是給一個元素“layout”。</P></BLOCKQUOTE><P class=blockquotesource>(Dean Edwards)</P><P> 有時候要解釋清楚某種行為是不可能的:就比如 hasLayout,會根據它的狀態選擇兩種不同渲染引擎的一種使用,而且每一種都有其自己的 bug 和怪異之處。</P><H4 id=absurd>不可消除的 bug</H4><BLOCKQUOTE title="Molly 說到..." cite=http://www.gunlaug.no/contents/molly_1_15.html><P> 軟體 bug 是由於在製作過程中對完整性和邏輯問題考慮不周等人為錯誤而導致的。這是人類的固有缺陷,目前還沒有什麼好的解決方案。</P><P> 同樣由於這種缺陷,任何試圖不重寫軟體而修複 bug 的做法,都將會不可避免的導致軟體中出現更複雜的bug。</P><P> 所有依賴別的軟體的軟體——當然包括依賴作業系統,也會同樣依賴他們的 bug。於是我們會從所有關聯的軟體中得到一連串的 bug,這也更說明找到一個無 bug 軟體是幾乎不可能的。</P></BLOCKQUOTE><P class=blockquotesource>(Molly, the cat?)</P><P> 本文建立於2005年6月30日,最後一次修改於2006年4月2日。<li ><i class="layui-icon"> |