一、引子
見下圖,我們有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
值不為auto
grid
容器的子元素,且z-index
值不為auto
opacity
屬性值小於1
的元素mix-blend-mode
屬性值不為normal
的元素;- 以下任意屬性值不為
none
的元素transform
filter
perspective
- 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/