前言:css
前不久,同事S遇到了一個關於position和z-index的問題。 他折騰了一天沒搞定,羣發了郵件尋求幫助, 我一開始覺得很簡單,就主動說幫忙,簡單嘗試以後,才發現貌似沒那麼簡單。 問題主要圍繞position和z-index。react
問題:wordpress
咱們項目用的是react,同事S有一個table,table裏有row,這個table用了一個第三方component,具體什麼component無所謂了,總之這些row都有一個css屬性:spa
positon: absolute;
而後,在每一個row中有個自定義的下拉框組件,下拉框點擊後,會彈出一個選項的容器,他指望選項(options)的容器div(暫稱div-options)在頁面的頂層,也就是不會被其餘東西遮蓋。可是結果就是被當前row以後的row給遮蓋了。div-options有以下css屬性:code
position: fixed;
問題的初始狀態相似以下例子:component
https://codepen.io/bee0060/pen/GGvMxb對象
在個人例子中,能夠簡單的對div-options添加大於0的z-index屬性解決。 只是在我同事的本地,貌似僅僅這樣沒法解決,具體緣由當時沒有意識到因此也就沒有細查。 不過我如今的猜想是,每一個row上面都有z-index屬性,相似以下例子:(沒錯,該例子中div-options會被後面的row遮蓋)blog
https://codepen.io/bee0060/pen/PaVwvjget
在發現只給div-options增長z-index沒法解決問題後,我也嘗試建議我同事給每一個row增長一個z-index,並且值小於div-options的z-index, 可是div-options依然被後面的row遮蓋。博客
當時我以爲真是難以想象,整我的都懵了。
後來試了幾種辦法沒成功就下班回家了。 在下班路上我好想意識到了什麼,就回家試了下,發現瞭如下兩種解決方案:
1. 在全部row有z-ind"Wx的狀況下, 給div-options所在的row一個較大的z-index值,這樣div-options無需z-index也不會被遮蓋 , https://codepen.io/bee0060/pen/eKepZG
2. 將div-options提取到table以外,讓他們沒有父子層級關係,再給一個較大的z-idnex也能夠解決,https://codepen.io/bee0060/pen/yEozro
在當前項目環境下,考慮到咱們用的是react,我建議我 同事採用第二個解決方案。由於在顯示div-options時去修改所在row的z-index,這個操做要影響的對象太多了,並且會對上級(父級)組件形成影響,感受不太好,要知道,影響的組件越多,就會觸發越多的diff和re-render,因此我仍是但願這個操做只須要通知儘量少的組件就能完成。
問題是解決了,可是到底這問題是怎麼引發的呢?
我查閱了一些資料, 下面的是個人一些分析和猜測,主要面向但願快速解決問題並得到一個大概思路的朋友。
若是須要更詳細準確的解釋,我須要查閱更多資料後另開一篇博客,或者大家能夠看下其餘更詳細的博客,正好這就有一篇我感受不錯的。
好,那麼下面我要開始表演了。
緣由分析:
首先,有些廢話仍是要說。
我列一下關鍵的分析依據:
1. 只有已經定位的元素(即position屬性值是非static的元素)的z-index屬性會生效
2. 在同一堆疊中,z-index值大的遮蓋 小的對象
3. 在同一堆疊中,若z-index值相同,後面的對象遮蓋前面的對象。
4. z-index爲數字(包括0)的元素會在當前堆疊上下文(暫稱爲堆疊A)中建立堆疊層級(暫稱堆疊B),堆疊B可視爲堆疊A的子級堆疊。
5. 一個對象只與處於相同堆疊上下文的對象進行z-index的比較並決定哪一個遮蓋哪一個。
6. 若A 和B對象處於同一 堆疊上下文,且根據z-index規則已肯定B顯示在A上面,那麼堆疊B及其全部子級堆疊都將顯示在堆疊A以及堆疊A的子級堆疊之上。(由於子級堆疊的顯示順序沒法超出父級堆疊的順序,不管z-index的數值多大)
上面屢次出現」堆疊「這個名詞,堆疊能夠理解爲css空間中,延Z軸方向互相層疊的多個小空間。 不少博客在圖例中把堆疊畫得像一張紙同樣,其實並不許確,由於堆疊中能夠有子級堆疊,裏面能夠有無限個層級,因此我以爲理解爲空間更合適。 能夠理解成較扁平的空間。 而css空間(或你叫DOM或HTML空間也能夠)中默認會建立一個堆疊。
看完上面幾條,估計你們也有頭緒了。 在css空間裏,若是對象沒有被定位(position不爲static)或已定爲但z-index不爲數字, 那麼該對象會被至於父級元素所在的堆疊以內,不然會在父級對象所在的堆疊中建立新的堆疊。 通常狀況下,若是頁面中全都是不會建立堆疊的對象(未定位或z-index不爲數字),那麼所有對象都將被置於默認堆疊內。
而每一個堆疊內的子堆疊都沒法超出其父堆疊的順序進行顯示,若是把堆疊理解成空間,這個應該比較好理解了。用修仙小說的話說就是:張三是某個世界裏的人,他再牛也不能超出你所在的世界(固然通常修真小說寫的都是能超出,你能理解個人話就好)。
那麼結合以前遇到的問題就好理解了。
- 默認狀況下,row只有position:absolute, 而都沒有z-index時,他們都處於默認堆疊中,且按row的先後順序進行遮蓋, 而div-options屬於某一個 row,按順序的話,他頂多和他所在的row使用一個顯示順序,因此天然會被後面的row遮蓋。
- 單獨給div-options添加大於0的z-index後, div-options會在默認堆疊(即全部row所在的堆疊)上建立堆疊層級,且因爲z-index大於0,在與處於同一父級堆疊中的全部row對象比較後,因爲row都沒有z-index,顯示順序和z-index爲0時相同,因此div-options會顯示全部在row之上,也就不會被遮蓋了。
- 在給row都增長一個數字的z-index(即便是0)以後,後續的rows又會遮蓋div-options。由於指定了數字z-index的row都會在父級堆疊中建立本身的堆疊,而div-options會在其父級row的堆疊內再建立堆疊。 而div-options的父級row(暫稱rowP)只會與處於同一個父級堆疊的其餘row的堆疊進行顯示順序比較, 因此後續row會顯示在rowP之上。而div-options是rowP的堆疊的子堆疊,因此不管div-options的z-index多大,都沒法超出rowP所在的堆疊。因此div-options會被後續rows遮蓋。
因此個人兩個解決辦法就是從兩方面 着手:
1. 給rowP更大的z-index,讓其顯示在全部兄弟rows之上,那麼div-options不管有無z-index都不會被後續row遮蓋。
2. 將div-options抽出來,再也不做爲rowP的子對象,也就不會再守rowP的堆疊順序影響,進而能夠實現讓div-options不被後續row遮蓋,由於div-options與全部rows能夠處於同一個父級堆疊中,誰先誰後就只由他們各自的z-index值決定。
好了,分析思考和猜想就到這了,原本想盡可能簡短,但仍是寫了那麼多。
最後說句最重要的: 若有謬誤,歡迎指正!
後話:
在解決過程當中,沒在網上找到該問題的相關資料’ 我(雖而後來找到了),並且本身以前研究也不深,本着DRY原則,避免下次遇到又要花時間折騰,故寫下此文。
參考資料:
2. 《CSS權威指南》