親愛的觀眾老爺們大家好~我發現我寫文章都是因為工作碰到問題才寫,什麼探索最前沿的最討厭了(其實是力有所不及)!言歸正傳,最近碰到的問題是這個:準備開發一個平臺,隨手寫導航欄元件之時,發現層級錯亂,無論如何調z-index
都無法達到預想的效果,大致程式碼如下:
<nav>
//背景遮蓋,絕對定位
<section class="mask"></section>
<ul>
<li>
balabala..
//次級列表,絕對定位
<ul class="menu">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
</li>
</ul>
</nav>複製程式碼
大致想要的效果就和這平臺的導航差不多。CSS程式碼就不貼了,我寫的時候碰到的問題就是無法將mask
絕對定位後的層級置於li
文字之下,又在使用了最對定位的menu
之上。當時為了趕需求,徵詢了產品的意見,改了實現的方式。但本著碰到問題不迴避的態度,通過兩天閒時廢(shi)寢(mian)忘(duo)食(meng)地查閱資料與瘋狂寫demo後,小結了點知識,在此分享給大家,希望對大家有所幫助。
本文主要是關於層疊上下文和層疊順序的相關知識,如果看官大人已經通曉,可能這篇文章幫不了你什麼,但你能幫我看下寫得有木有問題(手動滑稽)。
(友情提示,我寫這篇文章時,為了讓看官們更好地理解,寫得挺羅嗦的,如果直接想看結論,可以跳到最後~)
層疊上下文
先來點簡單的,我們先看”stacking context”,也就是層疊上下文。
層疊上下文是HTML元素的三維概念,這些HTML元素在一條假想的相對於面向(電腦螢幕的)視窗或者網頁的使用者的z軸上延伸,HTML元素依據其自身屬性按照優先順序順序佔用層疊上下文的空間。
上面的描述來自於MDN,簡單地說,就是幾個元素疊在一起,哪個放在“上面”。一般而言,某個元素一旦生成了層疊上下文,它會置於其他元素的上方,但這並不是絕對的,暫時請先記住這點。
根據MDN文件與自我試驗,生成層疊上下文的方式有以下幾種:
- 根元素 (HTML),
z-index
值不為 "auto"的 絕對/相對定位,- 一個
z-index
值不為auto
的flex
專案 (flex item),即:父元素display: flex|inline-flex;
, opacity
屬性值小於 1 的元素,transform
屬性值不為none
的元素,mix-blend-mode
屬性值不為normal
的元素,filter
值不為none
的元素,perspective
值不為none
的元素,isolation
屬性被設定為isolate
的元素,position: fixed
,- 在
will-change
中指定了任意 CSS 屬性,即便你沒有直接指定上述屬性的值, -webkit-overflow-scrolling
屬性被設定touch
的元素。
程式碼描述如下:
<!--HTML結構-->
<div class="div1"></div>
<div class="div2"></div>
/*css程式碼*/
div {
width: 100px;
height: 100px;
}
.div1 {
background: red;
}
.div2 {
background: blue;
margin-top: -50px;
}複製程式碼
這個時候畫風是這樣的:
沒毛病吧?
跟著你往div1
的CSS中加入上述隨意一個條件之一,比如opacity: .9;
,畫風就會變成:
也就是說由於紅色的div
生成了層疊上下文,從原來置於藍色的div
下方變為上方了。其他條件各位看官可以自行實現。
層疊水平
看起來還是蠻簡單的對吧?那現在來理解下一個概念:層疊水平。每個元素其實都有自己的層疊水平,不單是受z-index
影響的元素,不同元素之間其實是通過對比層疊水平來確定哪個在上面的。然而,層疊上下文除了讓元素的層級更高之外,還會生成一個獨立的上下文,它的子元素的層疊水平只在當前上下文生效。
簡單說,你要確定兩個元素哪個在上面,要先確定它們是否在同一個層疊上下文中,如果不在同一個上下文,那就找到在同一層疊上下文的祖先元素去“拼爹”。層疊水平的對比只在同一層疊上下文中才有意義。
看著還是蠻複雜,上程式碼感受下可能更清楚:
<!--HTML結構-->
<div class="div1">
<div class="div1Child"></div>
</div>
<div class="div2">
<div class="div2Child"></div>
</div>
/*css程式碼*/
.div1 {
/*isolation: isolate;*/
}
.div1Child {
background: red;
position: absolute;
z-index: 10;
}
.div2 {
margin-top: -50px;
/*isolation: isolate;*/
}
.div2Child {
background: blue;
position: absolute;
z-index: 1;
}複製程式碼
先來這樣的程式碼,複製到瀏覽器跑一下,可以看出是紅色在上面的。這很科學,因為紅色的z-index
更大嘛。跟著解除對isolation: isolate;
的註釋,可以看到藍色跑到紅色上面去了。那是因為.div1
與.div2
都生成了層疊上下文,它們的子元素z-index
再大也不會作用於上下文以外的元素。但如果子元素位置重疊了,那怎麼確定哪個在上面呢?那就去找它們的爸爸,直到找到處於同一層疊上下文(此例中的上下文是根元素形成的)的祖先元素(此例中是.div1
與.div2
),讓兩個祖先元素對比一下哪個位於上面就好了。不同層疊上下文的子元素進行對比一定是通過“拼爹”來確定的。
當然,也存在不是“拼爹”的情況,看看這麼一種情景:
<!--HTML結構-->
<div class="div1">
<div class="div1Child"></div>
</div>
<div class="div2">
<div class="div2Child"></div>
</div>
/*css程式碼*/
.div1 {
/*沒有形成層疊上下文*/
/*isolation: isolate;*/
}
.div1Child {
background: red;
position: absolute;
z-index: 10;
}
.div2 {
margin-top: -50px;
/*形成層疊上下文*/
isolation: isolate;
}
.div2Child {
background: blue;
position: absolute;
/*改動*/
z-index: 100;
}複製程式碼
思考一下這時候誰在上面?答案是紅色在上面。這是由於.div1
沒有形成層疊上下文,也就意味著.div1Child
形成了自己的層疊上下文,而且是在根元素的層疊上下文中起作用的,而.div2
也形成了自己的層疊上下文,所以.div2Child
不與外面的元素作對比層疊水平。此時上下關係對比的是.div1Child
與.div2
在根元素層疊上下文中層疊水平的對比。所以,要確認兩個元素哪個在上面,需要把它們拉到最近的一個層疊上下文中,和函式的作用域類似,只能向上找,不能往下找。
好像說得不太清楚,那就來個不恰當的比喻吧。想象整個根元素是一個大箱子,裡面有各種雜七雜八的子孫元素,但他們沒裝箱。它的每一個子孫元素,一旦形成了層疊上下文,那麼連上它的子元素,都就會被裝入一個小一點箱子(上述過程可以無限次執行,小箱子中有元素形成了層疊上下文,會獨立包成一個更小的箱子)。而且這些箱子都有黑科技加持,無論裡面的東西(層疊水平)多高,箱子看上都都是扁扁的(影響不了箱子外面)。同一個箱子內,哪個東西放最上面,那就看它的層疊水平咯。不同箱子中的東西想對比嗎?也可以啊,不過只能通過比較兩個箱子哪個放得高來決定了。希望這個比喻能幫你更好地理解上述概念。
暫時來說,我們可以得到的結論有這麼幾條:
- 通過新增某些CSS條件,可以形成層疊上下文。
- 形成層疊上下文的元素,層級高於其他元素。
- 層疊水平的對比,在相同的層疊上下文下才有意義。
有了上面的鋪墊,下面將迎來重頭戲:在同一層疊上下文中,不同箱子是按照什麼規則進行擺放的呢?就只有z-index
會產生影響嗎?箱子內雜七雜八那些東西,就沒個擺放順序嗎?
層疊順序
最後就是重頭戲啦,各種東西怎麼擺放的,都是有規則的,這就是層疊順序了。
先不談其他規則,其實根據之前的例子,已經能總結出最基本的規則:如果兩個元素在層疊順序中所在的位置一樣,那麼後來者居上。
比如這個:
<!--HTML結構-->
<div class="div1"></div>
<div class="div2"></div>
/*css程式碼*/
.div1 {
isolation: isolate;
background: red;
}
.div2 {
margin-top: -50px;
isolation: isolate;
background: blue;
}複製程式碼
藍色在上面,簡單易懂清晰明瞭~
而另一條基本規律,z-index
生效的情況下,值更大的排在上面,不用我貼程式碼了吧?相信看官大人已經實現了無數次這樣的場景了。
然而,思考這個如何:
<!--HTML結構-->
<div class="div1"></div>
<div class="div2"></div>
/*css程式碼*/
.div1 {
isolation: isolate;
background: red;
}
.div2 {
margin-top: -50px;
/*改變*/
position: relative;
background: blue;
}複製程式碼
還是藍色在上面哦!會不會覺得有點詫異呢?還記得我之前提到的:“一般而言,某個元素一旦生成了層疊上下文,它會置於其他元素的上方,但這並不是絕對的”這句話麼?這就是體現!結合剛才的規律,只有兩個情況可以解釋這個現象,要麼.div1
和.div2
處於同一位置,因此後來居上。要麼後者z-index
高於前者,然而我們沒有設定z-index
,也就是說不可能出現這情況。所以,規律是:某個元素形成了層疊上下文,那麼它在層疊順序中的位置與z-index
為0
或者auto
的元素相同。
然而這裡其實有點小坑的,我就掉進去了,也希望大家掉進去一次再爬出來,請看:
<!--HTML結構-->
<div class="div1">
<div class="div1Child"></div>
</div>
<div class="div2"></div>
/*css程式碼*/
.div1 {
background: red;
position: relative;
}
.div1Child {
background: yellow;
position: relative;
z-index: 1;
}
.div2 {
margin-top: -50px;
isolation: isolate;
background: blue;
}複製程式碼
你覺得什麼顏色會在最上面呢?
其實是黃色。依次的話是黃色->藍色->紅色。有沒有同學認為,既然你說某個元素形成了層疊上下文,那麼它在層疊順序中的位置與z-index
為0
或者auto
的元素相同;而且如果兩個元素在層疊順序中所在的位置一樣,那麼後來者居上。因此應該藍色最上面?我的傻孩子啊,其實你也對,你看,藍色不久在紅色上面嗎?完美對應這條規則。然而.div1Child
形成了自己的層疊上下文,是一個獨立的“小箱子”啊,在同一層疊上下文中,z-index
生效的情況下,值更大的排在上面,對吧?
踩進去又跳出來之後,後面的事情好辦不少了,考慮到篇幅問題,直接給大家說“箱子”內沒形成箱子的順序吧。
依次是display: line-block|inline|flex
的元素 -> 浮動元素 -> 塊狀元素 -> z-index
小於0的元素。
因而,結合全部規則,總體的排序如下面的程式碼實現(渣排版見諒):
<!--HTML結構-->
<div class="zIndexMoreThanZero">
<p>z-index`大於0的元素</p>
</div>
<div class="zIndexZero">
<p>等價於或本身就是`z-index`等於`0`或者`auto`的元素</p>
</div>
<div class="inlineBlock">
<p>display: line-block|inline|flex 的元素</p>
</div>
<div class="float">
<p>浮動元素</p>
</div>
<div class="block">
<p>塊狀元素</p>
</div>
<div class="zIndexLessThanZero">
<p>z-index 小於0的元素</p>
</div>
/*css程式碼*/
div {
width: 300px;
height: 100px;
margin-top: -30px;
box-shadow: 3px 3px 1px #999;
}
div p {
padding-top: 60px;
font-size: 8px;
}
.zIndexMoreThanZero {
margin-top: 0;
position: relative;
z-index: 1;
background: lightblue;
}
.zIndexZero {
isolation: isolate;
background: lightcoral;
}
.inlineBlock {
margin-left: -300px;
display: inline-block;
background: lightcyan;
}
.float {
margin-top: 40px;
float: left;
background: lightgoldenrodyellow;
}
.block {
margin-top: 40px;
background: lightgray;
}
.zIndexLessThanZero {
position: relative;
z-index: -1;
background: lightgreen;
}複製程式碼
瀏覽器頁面如圖:
事實上,還有最後一種順序的,有比z-index
小於0的元素更低的情況,那就是某元素形成了層疊上下文,它自己在自己形成的層疊上下文中,就是個墊底的存在了。程式碼例子如下:
<!--HTML結構-->
<div class="div1">
<div class="div1Child"></div>
</div>
/*css程式碼*/
.div1 {
background: red;
position: relative;
z-index: 10;
}
.div1Child1 {
background: yellow;
position: relative;
z-index: -1;
margin-left: 50px;
}複製程式碼
結果是黃色在上的。z-index
只作用於元素所處的層疊上下文,不作用於自己形成的層疊上下文。
至此,我瞭解到關於CSS 層疊相關的知識,就全部分享給大家啦。
小結
文章太長,直接看這其實也行,小結下來,關於CSS 層疊相關知識的規矩有這麼幾個:
- 由於某些CSS條件,會生成層疊上下文。
- 所有元素都有它的層疊水平,而層疊水平的對比只在同一層疊上下文中才有意義。
- 處於相同層疊順序的,後來居上,不相同的按序排放。
事實而言,相關的知識倒不是特別重要,就算不懂也不會導致無法交功課的狀況。只是,不希望自己把迴避問題變為習慣,而且瞭解下來還是蠻有趣的,之後再出現類似的狀況時,不至於束手無策。
感謝各位看官看到這裡,文章太長了~希望對大家有所幫助,我對層疊水平與層疊順序感覺不是特別透徹。如有不當之處,還請不吝賜教!