關於 z-index,你可能一直存在誤區

  • 原文地址:What You May Not Know About the Z-Index Property
  • 原文做者:Steven Bradley
  • 譯者:Chor

z-index 這個屬性表面看上去很簡單,但若是你想搞清楚其工做原理的話,實際上是有很多值得探討之處的。本文將從層疊上下文(stacking contexts)和一些實際案例出發,談一談 z-index 的內部工做原理。css

CSS 爲盒模型的佈局提供了三種不一樣的定位方案[1] :html

  • 正常文檔流
  • 浮動
  • 定位

最後一種方案(特指絕對定位)將會把元素從正常文檔流中徹底移走,其最終的落腳點將取決於開發者。前端

經過設置 topleftbottom 和 right 的值,你能夠在二維空間中對元素進行定位,但 CSS 同時也容許你使用 z-index 屬性[2] 把它放置在三維空間中。web

表面看起來,z-index 彷佛是一個很簡單的屬性,你給它設置哪一個值,元素就會位於 y 軸的哪一個位置,就這樣。但它實際上並無咱們想象的這麼簡單,這個屬性背後是一系列決定元素所在層級的規則。微信

爲了保證咱們在同一個「頻道」上,這裏我先普及一些基礎概念,以後再解釋層疊的相關知識,並在一些場景中體會  z-index[3] 做用的機制。app

Z-Index 的基礎概念

對於三維空間座標系,你確定很熟悉了。x 軸表明水平方向,y 軸表明垂直方向,z 軸則表明咱們的目光向頁面(屏幕)看進去的時候,各元素的佈局狀況。編輯器

因爲屏幕是一塊二維平面,咱們實際上並無真的看到 z 軸,更多的是經過透視的形式。具體地說,多個元素共享同一塊二維平面時,有的元素在頂部,有的元素在底部,咱們由此感覺到了 z 軸的存在。ide

爲了決定某個元素在 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 位於頂層,這是由於它的層疊等級更高。

雖然上面這個例子只涉及到了兩個層疊等級,但實際上,在一個層疊上下文中,一共可能出現七個層疊等級,從最低到最高排列,依次是:

  1. 背景和邊框 :造成層疊上下文的元素的背景和邊框,它是整個上下文中層疊等級最低的。
  2. Z-Index 爲負數 :設置了  z-index 爲負數的子元素以及由它所產生的層疊上下文
  3. 塊級盒模型:位於正常文檔流中的、塊級的、非定位的子元素
  4. 浮動盒模型 :浮動的、非定位的子元素
  5. 內聯盒模型 :位於正常文檔流中的、內聯的、非定位的子元素
  6. Z-index 爲 0:設置了  z-index 爲 0 的、定位的子元素以及由它所產生的層疊上下文
  7. Z-Index 爲正數 :設置了  z-index 爲正數的、定位的子元素以及由它所產生的層疊上下文,它是整個上下文中層疊等級最高的

這七個層疊等級就構成了層疊順序的規則。符合層疊等級七的元素,會比層疊等級在一到六的元素更「貼近咱們」,符合層疊等級五的元素,會比層疊等級二的元素更「貼近咱們」,以此類推。

第一次學習這些層疊規則的時候,我感受收穫了不少新的東西。若是隻着眼於層疊等級2、六和七(也就是涉及到 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 {
  width200px;
  height200px;
  padding20px;
}
 
.one.two.three.four {
  position: absolute;
}
  
.one {
  background#f00;
  outline5px solid #000;
  top100px;
  left200px;
  z-index10;
}
  
.two {
  background#0f0;
  outline5px solid #000;
  top50px;
  left75px;
  z-index100;
}
 
.three {
  background#0ff;
  outline5px solid #000;
  top125px;
  left25px;
  z-index150;
}
 
.four {
  background#00f;
  outline5px solid #ff0;
  top200px;
  left350px;
  z-index50;
}

儘管 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/

點個『在看』支持下 


本文分享自微信公衆號 - 前端技術江湖(bigerfe)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索