你從未瞭解過的 z-index

以前在使用 z-index 的時候,心中隱約自以為是的認為就單純 z-order,不就是個絕對數值,就值越小深度越往下。實際上在實作的時候常常不如預期,不過使用試誤法還是可以解決,只是一直沒有瞭解原理有點不踏實,但慢慢已經發現這件事情沒有想像中的這麼簡單。

最近剛好讀到一篇文章,覺得解釋得不錯,所以就來翻譯一下這篇文。

大部分的人都沒有真正的理解 z-index 是如何運作。z-index 並不複雜,但如果你從未花時間去讀 spec ,那麼你肯定有些你完全沒有意識到的關鍵點。不相信的話來看看你有沒有辦法解這個問題:

問題

底下這個 Sample 裡面包含了三個 div ,每一個 div 中包含一個 span ,並且分別給予三個 span 紅、藍、綠三個顏色。每個 span 都是 absolute position,並且第一個紅色 span 額外設定了 z-index=1。

Check out this Pen!

好的,那麼現在有一個挑戰是想辦法把紅色的 span 放到最下面,並且符合以下規則:

  • 不能更動 HTML
  • 不能對任何 element 新增或是改變 z-index
  • 不能新增或改變任何 element 的 position

有興趣挑戰的話可以按一下上面的 Edit on Codepen 嘗試看看。如果你成功的解決這個問題,那代表你應該可以跳過以下文章了 🙂 提醒:不要按下面的 CSS 不然你就會直接看到解答了。

Check out this Pen!

 

解答

方法是將第一個 div ( red span 的爸爸) 設定一個小於 1 的 opacity。

div:first-child {
    opacity: .99;
}

如果你不懂 first-child 的話,其實你也可以直接在第一個 div 上給 inline style: stye=”opacity:0.99″。 但我想不懂 first-child 的話應該不會來看這篇文章才對。 如果你正想破頭不知道為什麼,而且無法相信 opacity 會影響 element 的深度的話,嗯,你並不孤獨,我第一次遇到這個問題的時候也一樣的驚訝。 希望以下內容能夠幫助釐清這個問題與解答。

疊放順序 (Stacking Order)

Z-index 看起來似乎非常簡單:具有較高 z-index 的 element 會疊放在具有較低 z-index 的 element 上。但實際上不是的。z-index 的一部分問題就在於它看起來很簡單以至於大部份的開發者不會花時間去研讀這些規則。 疊放順序指的是 HTML document 中的每一個 element 可以被放在其他 element 的前面或是後面。而決定這個順序的規則很明確的寫在 spec 中。但如同前面所述,大部分的開發者並不會去看 spec 來完整的了解這個規則。 對於沒有指定 z-index 與 position 的 element 來說,基本上疊放順序就相等於在 HTML 中出現的順序 (好吧,其實也沒那麼單純,但如果你沒有使用小於零的 margin 的話,這應該還是基本上成立)。 當把 position 放進來考慮的時候,那麼所有具有 position 屬性的 element 會被堆放在沒有 position 屬性的 element 上 (這裡說的沒有 position 屬性,正確地來說應該是沒有明確指定而是預設的 static position value)。 最後,牽涉到 z-index 後,這順序的決定就會變得有點難處理了。首先 z-index 只對具有非 static position value 的 element 有效,也就是說 element 上必須是 relative 或是 absolute position,那設定在這 element 上的 z-index 才會有作用。再來是 z-index 的設定會創造疊放群組(stacking context),於是看似簡單的規則就因為這個 stacking context 而變得複雜了。

疊放群組 (Stacking Contexts)

疊放群組 (stacking contexts) 指的是一組具有相同父元素的 elements 具有相同的疊放順序 (stacking order) 。能夠真正理解 z-index 以及疊放順序 (stacking order) 如何運作的真正關鍵是充分的了解 疊放群組 (stacking contexts) 。 每一個疊放群組 (stacking contexts) 會有一個單一的 DOM element 做為根元素。當一個元素構成一個新的疊放群組 (stacking contexts)的時候,所有該元素的所有子元素都會被歸類在在疊放順序(stacking order)中的同一個位置。也就是說如果一個元素被包含在一個相對疊放順序較低的 stack context 中,那麼即使你設定它的 z-index 為一千萬也沒有辦法再讓它出現在其他具有較高疊放順序(stacking order) 的疊放群組 (stacking contexts) 上面。 以下三種情況會產生新的 疊放群組 (stacking contexts)

  • 當一個 element 是文件的根節點的時候 (也就是 <html>)
  • 當一個 element 有非 static value 的 position 屬性以及非 auto 的 z-index 屬性
  • 當一個 element 具有小於 1 的 opacity

前兩個合理並且一般開發者應該都懂 (即使不知道它們叫什麼)。 但第三個 (opacity) 就幾乎從來沒有在 W3C Spec 以外的地方被提起過。

決定元素的疊放順序 (Stacking Order)

事實上,決定全域的所有元素在一個頁面中(包含邊框、背景、以及文字節點等等)的疊放順序 (Stacking Order) 是極度的複雜並且遠超過本文所要討論的範疇。(有興趣的話還是建議參考 spec) 但一個基本的了解在大多數情況下還是可以幫助讓 CSS 的開發是比較符合規範的。

疊放群組 (stacking contexts)中的疊放順序 (Stacking Order)

以下所示是基本規則,這邊直接使用對應的 CSS 設定來解釋,但有一點需要注意的是因為 transform 會導致不同的 z-order,但這邊並未包含 transform 之後的規則。

Check out this Pen!

這邊有一點有趣的是具有負值的 z-index 元素會排序在疊放順序中較底的位置,因此這使得這樣的元素有可能會被自己的 parent 蓋住。關於這部分可以參考這個由 Nicolas Gallagher 所提供的很棒的範例

全域的疊放順序 (Stacking Order)

對於疊放群組 (Stacking Context) 以及單一群組中的疊放順序有了比較具體地瞭解之後,對於全域的順序的理解就沒有這麼難了。 要避免被 z-index 困住的關鍵在於能夠標注出疊放群組的所在元素。當你對一個元素設定了上千萬的 z-index 但它的順序還是不受任何影響的話,那麼看看它的子節點或是父節點看看是不是其中一個構成了新的疊放群組 (Stacking Context)。如果有的話,那上千萬的 z-index 是不會有任何幫助的。

總結

回到原始的問題,以下是對應的 HTML 原始碼並標注上對應的疊放順序 (stacking order)在解法中我們對第一個 div 設定了 opacity 小於 “1″ 之後,疊放順序 (stacking order) 就變成了
1.1 指的是一個新的疊放群組 (Stacking Context)形成,於是 red span 被歸類在同一個 order 中。

(資料來源:http://philipwalton.com/articles/what-no-one-told-you-about-z-index/ by Philip Walton)

 



Comments
  1. JImmy Tien
    回覆

Leave a Reply

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *