- 原文位址:What You May Not Know About the Z-Index Property
- 原文作者:Steven Bradley
- 譯者:Chor
z-index
這個屬性表面看上去很簡單,但如果你想搞清楚其工作原理的話,其實是有不少值得探讨之處的。本文将從層疊上下文(stacking contexts)和一些實際案例出發,談一談
z-index
的内部工作原理。
CSS 為盒模型的布局提供了三種不同的定位方案[1] :
- 正常文檔流
- 浮動
- 定位
最後一種方案(特指絕對定位)将會把元素從正常文檔流中完全移走,其最終的落腳點将取決于開發者。
通過設定
top
,
left
,
bottom
和
right
的值,你可以在二維空間中對元素進行定位,但 CSS 同時也允許你使用 z-index 屬性[2] 把它放置在三維空間中。
表面看起來,
z-index
似乎是一個很簡單的屬性,你給它設定哪個值,元素就會位于 y 軸的哪個位置,就這樣。但它實際上并沒有我們想象的這麼簡單,這個屬性背後是一系列決定元素所在層級的規則。
為了保證我們在同一個“頻道”上,這裡我先普及一些基礎概念,之後再解釋層疊的相關知識,并在一些場景中體會 z-index[3] 作用的機制。
Z-Index 的基礎概念
對于三維空間坐标系,你肯定很熟悉了。x 軸代表水準方向,y 軸代表垂直方向,z 軸則代表我們的目光向頁面(螢幕)看進去的時候,各元素的布局情況。
由于螢幕是一塊二維平面,我們實際上并沒有真的看到 z 軸,更多的是通過透視的形式。具體地說,多個元素共享同一塊二維平面時,有的元素在頂部,有的元素在底部,我們由此感受到了 z 軸的存在。
為了決定某個元素在 z 軸方向上的位置,CSS 允許我們為 z-index 屬性設定三種值[4]:
- auto(預設值)
- 整數
- inherit
我們主要看一下整數值。它可以是正整數、負整數或者 0,值越大,元素就離我們“越近”,值越小,元素自然也就離我們“越遠”。
如果兩個元素在定位之後共享同一塊二維空間,那麼在這塊空間中,
z-index
越大的元素将可能覆寫
z-index
較小的元素。
很顯然,上面講的這些都是非常容易了解的,并且也和我們的直覺相符合。不過,下面的問題恐怕就不是很好回答了:
- 當設定了定位和
的元素與一個位于正常文檔流中的元素重疊時,哪一個在頂層呢?z-index
- 一個元素設定定位,另一個元素設定浮動,哪一個在頂層呢?
- 如果父元素和子元素都設定了定位,會發生什麼事?
為了更好地理清這些問題,我們有必要進一步了解與
z-index
工作原理相關的一些概念,也就是層疊上下文、層疊等級和層疊順序。
層疊上下文和層疊等級
針對層疊上下文和層疊等級[5] ,可能很難給出一個清晰易懂的概念,是以我們這裡用通俗的例子來了解。想象一下,現在有一張桌子,上面擺滿了各種東西。那麼這張桌子就代表了一個層疊上下文,假設還有另一張與之并排的桌子,那麼就産生了另一個層疊上下文。
如圖所示,層疊上下文 1 指的就是文檔根部,而層疊上下文 2 和 3 位于 1 的某個層疊等級中。此外,這兩個層疊上下文各自會包含新的層疊等級。
現在想象一下,第一張桌子上面并排擺了四個磚頭,這四個磚頭上面放着一個玻璃杯,而玻璃杯上面還放着一個水果盤。那麼,磚頭、玻璃杯、水果盤,各自都處于不同的層疊等級中,但它們共處于“桌子”這一層疊上下文中。
對每一個網頁來說,預設都會建立一個層疊上下文[6] ,這個上下文(這張桌子)的根部就是
html
元素,
html
元素的所有子元素都會位于這個預設的層疊上下文中的某個層疊等級,就好比東西會擺放在桌子的不同位置上一樣。
當你給某個元素設定一個非
auto
的
z-index
時,就會建立一個新的 層疊上下文[7] ,它和它所包含的層疊等級都是獨立于其它層疊上下文和層疊等級的,就好比你搬了一張新的桌子放在房間裡,它和舊的桌子是互相獨立的。
層疊順序
我們可以通過一個非常簡單的例子來了解層疊順序,這個例子甚至還不需要涉及到 定位元素[8] 。
想象一下,現在有一個非常簡單的網頁,不考慮預設的
<html>
,
<head>
,
<body>
等元素,就隻需要考慮每個網頁至少都會有的一個
<div>
。在 CSS 檔案中設定
html
的背景顔色為藍色,設定
div
的背景顔色為紅色,并設定寬高。
當加載頁面的時候,你覺得會看到什麼?
這個自然不用多想,引入眼簾的肯定是一大片的藍色,同時還有一個此前設定好尺寸的紅色塊級元素。除非你做了額外的設定,否則這個元素将正常地出現在左上角。
你可能會說“就這?太簡單了吧”,不過有一個問題可能不那麼簡單:為什麼紅色的塊級元素就一定會位于藍色背景的上層呢?為什麼我們看到的就是
div
位于
html
的上層呢?原因是,它們都遵循了層疊順序的規則。
在這個簡單的例子中,根據規則,正常文檔流的子塊(
div
)的層級将會高于根元素(
html
)的背景和邊框。我們看到
div
位于頂層,這是因為它的層疊等級更高。
雖然上面這個例子隻涉及到了兩個層疊等級,但實際上,在一個層疊上下文中,一共可能出現七個層疊等級,從最低到最高排列,依次是:
- 背景和邊框 :形成層疊上下文的元素的背景和邊框,它是整個上下文中層疊等級最低的。
- Z-Index 為負數 :設定了
為負數的子元素以及由它所産生的層疊上下文z-index
- 塊級盒模型:位于正常文檔流中的、塊級的、非定位的子元素
- 浮動盒模型 :浮動的、非定位的子元素
- 内聯盒模型 :位于正常文檔流中的、内聯的、非定位的子元素
- Z-index 為 0:設定了
為 0 的、定位的子元素以及由它所産生的層疊上下文z-index
- Z-Index 為正數 :設定了
為正數的、定位的子元素以及由它所産生的層疊上下文,它是整個上下文中層疊等級最高的z-index
這七個層疊等級就構成了層疊順序的規則。符合層疊等級七的元素,會比層疊等級在一到六的元素更“貼近我們”,符合層疊等級五的元素,會比層疊等級二的元素更“貼近我們”,以此類推。
第一次學習這些層疊規則的時候,我感覺收獲了很多新的東西。如果隻着眼于層疊等級二、六和七(也就是涉及到
z-index
的等級),那麼大部分時候,我們對于
z-index
的了解是正确的。正的
z-index
的層級比 0 要高,而 0 又比負的要高,一切都符合直覺,可能大多數人到這裡就不繼續往後探究了。
我之前就是這樣,在看到這些規則之前,以為除了正的和負的
z-index
,其它情況都可以看作是
z-index
為0 —— 不過現在我們很清楚了,這種想法是錯誤的。事實是,大部分元素的層級都要低于
z-index:0
。
還有一個有趣的細節是,非定位的元素實際位于四種不同的層疊等級中。乍一想覺得很奇怪,不過其實這是很合理的。假設所有的非定位元素都位于同一個層疊等級,那麼我們就沒辦法在
div
(塊級盒)上看到文本(内聯盒)了。
來看個案例
我前面提到過很多次,當你給一個元素設定非 auto 的
z-inde
時,會建立一個新的、完全獨立的層疊上下文。
重新回顧一下之前拿桌子做比喻的案例。一開始的時候,我們的桌子上擺滿了四塊磚頭,上面是一個玻璃杯,再上面是一個水果盤。現在,假設又有一張新的桌子,它擺放的東西和舊桌子差不多,唯一的不同是,新桌子少了一個水果盤。
不難想象,舊桌子的水果盤是整個房間中位于最頂層的物品(它有最大的
z-index
),不過,如果把舊桌子以及它上面的所有東西整體搬到地下室呢?此時,水果盤的層級會比新桌子上的每一個物品都要低,這是因為,放置水果盤的舊桌子整體已經低于新桌子了。
對于網頁上的定位元素來說,其實道理是一樣的。假設有如下代碼,思考一個問題:
div.two
和
div.four
,哪個在上哪個在下?
HTML:
<div class="one">
<div class="two"></div>
<div class="three"></div>
</div>
<div class="four"></div>
CSS:
div {
width: 200px;
height: 200px;
padding: 20px;
}
.one, .two, .three, .four {
position: absolute;
}
.one {
background: #f00;
outline: 5px solid #000;
top: 100px;
left: 200px;
z-index: 10;
}
.two {
background: #0f0;
outline: 5px solid #000;
top: 50px;
left: 75px;
z-index: 100;
}
.three {
background: #0ff;
outline: 5px solid #000;
top: 125px;
left: 25px;
z-index: 150;
}
.four {
background: #00f;
outline: 5px solid #ff0;
top: 200px;
left: 350px;
z-index: 50;
}
盡管
div.two
有更高的
z-index
(100),但在頁面上,它的層級實際上比
div.four
(
z-index
為50)要低。下圖就是頁面元素的層級情況,根據黑色和黃色邊框,我們可以區分每個元素生成的不同的層疊上下文。
由于
div.two
位于
div.one
中,是以它的
z-index
是和
div.one
的層疊上下文相關的,也就是說,實際表現出來的
z-index
是下面這樣的:
- .one —— z-index = 10
- .two —— z-index = 10.100
- .three —— z-index = 10.150
- .four —— z-index = 50
div.one
和内部包含的一切将會在層級上低于
div.four
,無論給
div.one
的子元素設定多大的
z-index
,子元素的層級都無法超過
div.four
。
看到這個例子是不是有一種熟悉的味道?我也曾經被
z-index
這麼坑過一兩次。我們都曾疑惑一個問題,為什麼一個
z-index
非常大的元素,在層級上始終無法超過一個
z-index
比它小很多的元素?相信在學習了這些案例之後,你已經豁然開朗了。
總結
在最初學習
z-index
的時候,我們都會認為它很簡單,這不就是代表元素在 z 軸上的位置嗎?不過我們現在知道了,事情遠沒有這麼簡單。深入了解 z-index[9] 一文也揭示了
z-index
背後一些原理層面的東西,包括層疊上下文,層疊等級以及一系列決定元素的層疊順序的規則。
最後,記住一個很重要的結論:定位元素可以建立新的層疊上下文,在這個上下文中的所有層疊等級,都會高于或者低于另一個層疊上下文的所有層疊等級。
拓展閱讀
- What No One Told You About Z-Index by Philip Walton
- The Z-Index CSS Property: A Comprehensive Look on Smashing Magazine
- How Well Do You Understand CSS Positioning?
- The stacking context on Mozilla Developer Network
- Z-Index And The CSS Stack: Which Element Displays First?
參考資料
[1]
定位方案: https://www.w3.org/TR/CSS2/visuren.html#positioning-scheme
[2]
z-index 屬性: http://www.w3.org/TR/CSS2/visuren.html#layers
[3]
z-index: http://www.vanseodesign.com/css/css-stack-z-index/
[4]
z-index 屬性設定三種值: http://www.w3.org/TR/CSS21/visuren.html#propdef-z-index
[5]
層疊上下文和層疊等級: http://timkadlec.com/2008/01/detailed-look-at-stacking-in-css/
[6]
層疊上下文: https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Understanding_z_index/The_stacking_context
[7]
層疊上下文: http://www.w3.org/TR/CSS21/zindex.html
[8]
定位元素: http://www.vanseodesign.com/css/css-positioning/
[9]
深入了解 z-index: http://coding.smashingmagazine.com/2009/09/15/the-z-index-css-property-a-comprehensive-look/