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:設置了 z-index
爲 0 的、定位的子元素以及由它所產生的層疊上下文 -
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?
原文地址:What You May Not Know About the Z-Index Property
作者:Steven Bradley
譯者:Chor
回覆“加羣”與大佬們一起交流學習~
點擊“閱讀原文”查看 120+ 篇原創文章
本文分享自微信公衆號 - 前端自習課(FE-study)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。