一、引子
見下圖,我們有one、two、three三個div。
one嵌套了一個nested的div。
nested的z-index等於100,two的z-index只等於1。

但是nested卻仍然被two覆蓋。
Why?
別着急,我們慢慢剖析。
二、正常的元素順序
我們新建三個div:
<div>one</div>
<div>two</div>
<div>three</div>
頁面效果展示如下(代碼見附錄A):

正常的文檔流中,元素次序是跟元素在文檔中出現次序一致。
three出現最后,次序最高,覆蓋掉two。
以此類推,two出現比one稍晚,所以覆蓋住one。
三、positioned元素脫穎而出
當我們為two加上position:relative后(代碼見附錄B):

two脫穎而出,覆蓋住了one和three。
這是因為,瀏覽器先繪制non-positioned元素,再繪制positioned元素。
non-positioned元素 指
position屬性值為默認值static,那么不為static的就是 positioned元素 。
四、都是positioned元素呢?
把three也設置為position: relative的話(代碼見附錄C):

變回老樣子,positioned元素次序和在文檔出現次序一致。
想要讓two再次脫穎而出,那考慮z-index吧!
五、z-index的用處
當把two設置為z-index: 1后(代碼見附錄D):

通過z-index讓two排到了前面。
“Z”代表X-Y-Z笛卡爾坐標系中的深度。
z-index數值高的元素會排在z-index數字較低的元素之前。
使用z-index要注意兩個陷阱:
1. z-index只應用於positioned元素
2. 隱式地創建一個 層疊上下文(stack context)。
🚀 注意, 層疊上下文和BFC(Block Formatting Context)有所區別:
層疊上下文解決元素次序問題(一個元素是否排在另一個元素之前),而BFC解決文檔流和一個元素是否會被重疊。
六、 層疊上下文
當你在一個positioned元素上應用z-index,會隱式創建一個全新的 層疊上下文,這個元素同時成為了該 層疊上下文的根元素。
上節的two就是一個 層疊上下文的根元素。
z-index只控制當前層疊上下文的元素次序。
我們把one和two設為position: relative,並且z-index都為1。
並且在one中內嵌一個nested的div,nested設置(具體代碼見附錄E):
position: absolute;
z-index: 100;
結果展示的就是我們開頭所提到的:

雖然nested的z-index為100,可是還是被two所覆蓋。
這是因為,nested和two分別屬於兩個不同的層疊上下文。
z-index只控制自己當前層疊上下文的元素次序。
七、 深入層疊上下文
除了z-index會創建層疊上下文,opacity的值低於1也會創建,還有transform和filter也會,全部請詳見附錄F。
層疊上下文中元素的次序按照以下規則:
1. 層疊上下文的根元素
⬇️
2. z-index為負數的positioned元素
⬇️
3. Non-positioned元素
⬇️
4. z-index為auto的positioned元素
⬇️
5. z-index為正數的positioned元素
非必要不創建層疊上下文⚠️
在上一節的例子中,我們就感受到了,多個層疊上下文,一方面會造成混亂,另一方面會使我們受挫,達不到想要的效果。
八、 消除z-index魔術字的技巧
魔術字(magic number)指使用直接的數字字面量,這個值背后所代表的意思是隱藏着的,就像魔術一樣。
在之前的例子中,z-index為1,或者為100,都可以算魔術字。
消除魔術字,最好的方法是用變量規范起來。
比如,我們用變量去規范程序中各個z-index的值:
--z-loading-indicator: 100;
--z-nav-menu: 200;
--z-dropdown-menu: 300;
--z-modal-backdrop: 400;
--z-modal-body: 410;
最后
看到這里,恭喜你完成了z-index的學習 🎉🎉🎉
更多文章可以關注公眾號『鵬哥兒的Echo』
生命不息,筆耕不輟

附錄A
HTML代碼
<body>
<div class="box one">one</div>
<div class="box two">two</div>
<div class="box three">three</div>
</body>
CSS代碼
body {
margin: 40px;
}
.box {
display: inline-block;
width: 200px;
line-height: 200px;
text-align: center;
border: 2px solid black;
background-color: #ea5;
margin-left: -60px;
vertical-align: top;
}
.one {
margin-left: 0;
}
.two {
margin-top: 30px;
}
.three {
margin-top: 60px;
}
附錄B
HTML代碼
<body>
<div class="box one">one</div>
<!-- 增加一個class名 -->
<div class="box two positioned">two</div>
<div class="box three">three</div>
</body>
CSS代碼
body {
margin: 40px;
}
.box {
display: inline-block;
width: 200px;
line-height: 200px;
text-align: center;
border: 2px solid black;
background-color: #ea5;
margin-left: -60px;
vertical-align: top;
}
.one {
margin-left: 0;
}
.two {
margin-top: 30px;
}
.three {
margin-top: 60px;
}
/* 增加設置position值 */
.positioned {
position: relative;
background-color: #5ae;
}
附錄C
HTML代碼
<body>
<div class="box one">one</div>
<div class="box two positioned">two</div>
<!-- three也設置為positioned -->
<div class="box three positioned">three</div>
</body>
CSS代碼
body {
margin: 40px;
}
.box {
display: inline-block;
width: 200px;
line-height: 200px;
text-align: center;
border: 2px solid black;
background-color: #ea5;
margin-left: -60px;
vertical-align: top;
}
.one {
margin-left: 0;
}
.two {
margin-top: 30px;
}
.three {
margin-top: 60px;
}
/* CSS其實不變 */
.positioned {
position: relative;
background-color: #5ae;
}
附錄D
HTML代碼
<body>
<div class="box one">one</div>
<div class="box two positioned">two</div>
<div class="box three positioned">three</div>
</body>
CSS代碼
body {
margin: 40px;
}
.box {
display: inline-block;
width: 200px;
line-height: 200px;
text-align: center;
border: 2px solid black;
background-color: #ea5;
margin-left: -60px;
vertical-align: top;
}
.one {
margin-left: 0;
}
.two {
margin-top: 30px;
}
.three {
margin-top: 60px;
}
.positioned {
position: relative;
background-color: #5ae;
}
/* 設置z-index */
.two{
z-index: 1;
}
附錄E
HTML代碼
<body>
<div class="box one positioned">
one
<div class="absolute">nested</div>
</div>
<div class="box two positioned">two</div>
<div class="box three">three</div>
</body>
CSS代碼
body {
margin: 40px;
}
.box {
display: inline-block;
width: 200px;
line-height: 200px;
text-align: center;
border: 2px solid black;
background-color: #ea5;
margin-left: -60px;
vertical-align: top;
}
.one {
margin-left: 0;
}
.two {
margin-top: 30px;
}
.three {
margin-top: 60px;
}
.positioned {
position: relative;
background-color: #5ae;
z-index: 1;
}
.absolute {
position: absolute;
top: 1em;
right: 1em;
height: 2em;
background-color: #fff;
border: 2px dashed #888;
padding: 1em;
line-height: initial;
z-index: 100;
}
附錄F
滿足以下任意條件可創建層疊上下文[2]:
- 文檔根元素(
<html>); position屬性值為absolute或relative,並且z-index不為auto的元素;position屬性值為fixed或sticky的元素flex容器的子元素,且z-index值不為autogrid容器的子元素,且z-index值不為autoopacity屬性值小於1的元素mix-blend-mode屬性值不為normal的元素;- 以下任意屬性值不為
none的元素transformfilterperspective- clip-path
mask/ mask-image /mask-border
isolation屬性值為isolate的元素-webkit-overflow-scrolling屬性值為touch的元素- 值設定了任一屬性而該屬性在
non-initial值時會創建層疊上下文的元素(參考這篇文章[3]) contain屬性值為layout、paint或包含它們其中之一的合成值,比如(contain: strict、contain: content)的元素
Reference
[1] CSS In Depth: 7.4 Stacking contexts and z-index
[3] Everything You Need to Know About the CSS will-change Property: https://dev.opera.com/articles/css-will-change-property/
