[CSS]关于z-index与position的一次奇异经历
前言:
前不久,同事S遇到了一個關于position和z-index的問題。?他折騰了一天沒搞定,群發了郵件尋求幫助,?我一開始以為很簡單,就主動說幫忙,簡單嘗試之后,才發現貌似沒那么簡單。?問題主要圍繞position和z-index。
?
?
問題:
我們項目用的是react,同事S有一個table,table里有row,這個table用了一個第三方component,具體什么component無所謂了,總之這些row都有一個css屬性:
positon: absolute;然后,在每個row中有個自定義的下拉框組件,下拉框點擊后,會彈出一個選項的容器,他期望選項(options)的容器div(暫稱div-options)在頁面的頂層,也就是不會被其他東西遮蓋。但是結果就是被當前row之后的row給遮蓋了。div-options有如下css屬性:
position: fixed;問題的初始狀態類似如下例子:
https://codepen.io/bee0060/pen/GGvMxb
?
在我的例子中,可以簡單的對div-options添加大于0的z-index屬性解決。?只是在我同事的本地,貌似僅僅這樣無法解決,具體原因當時沒有意識到所以也就沒有細查。?不過我現在的猜測是,每個row上面都有z-index屬性,類似如下例子:(沒錯,該例子中div-options會被后面的row遮蓋)
https://codepen.io/bee0060/pen/PaVwvj
?
在發現只給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原則,避免下次遇到又要花時間折騰,故寫下此文。
?
?
參考資料:
1.?深入理解CSS中的層疊上下文和層疊順序
2. 《CSS權威指南》
3. https://developer.mozilla.org/zh-CN/docs/Web/CSS/z-index
?
轉載于:https://www.cnblogs.com/bee0060/p/9191978.html
總結
以上是生活随笔為你收集整理的[CSS]关于z-index与position的一次奇异经历的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Web App架构
- 下一篇: BZOJ2437 [Noi2011]兔兔