前言
石匠敲擊石頭的第 15 次
在平常開發的時候,有時候會遇到使用 z-index
調整元素層級沒有效果的情況,究其原因還是因為對層疊上下文不太了解,看了網上很多前輩的文章,決定打算寫一篇文章來梳理一下,如果哪里寫的有問題歡迎指出,不勝感激。
什么是層疊上下文
層疊上下文(Stacking Context)是 HTML 中的三維概念,它決定了元素在 Z 軸(垂直于電腦屏幕的方向) 上的顯示順序。簡單來說,層疊上下文是瀏覽器用來組織元素 Z 軸方向堆疊順序的一個獨立的 “視覺區域” 或 “作用域”。
層疊上下文的概念有點類似于塊級格式化上下文(BFC),都是在特定條件下由元素創建的獨立作用區域,并且區域內遵循各自的規則,但它作用在視覺層級(z-index
)而非布局流。
什么是層疊水平
在一個層疊上下文中,層疊水平(Stacking Level,也叫層疊等級) 用于確定其子元素在 Z 軸方向上的顯示優先級,簡單來說,層疊水平決定了同一層疊上下文內的元素誰在上、誰在下。
?? 注意:
- 元素的層疊水平是由其所屬的層疊上下文來決定的,也就是說層疊水平的比較只有在同一個層疊上下文中才有意義,所以不同層疊上下文之間的元素不會互相影響
- 某些情況下(Flex 子元素和定位元素)
z-index
確實可以影響元素的層疊水平,但層疊水平不等于z-index
,所有元素都有層疊水平
什么是層疊順序
層疊上下文和層疊順序這兩個終究只是抽象的概念,但元素具體是按照什么規則層疊的,這就不得不提層疊順序(Stacking Order)。
簡單來說,層疊順序就是指當多個元素在 Z 軸方向發生重疊時,瀏覽器決定哪些元素顯示在上、哪些元素被遮擋的一套規則。
上圖展示了在不考慮 CSS3 新特性(如 flex
、grid
、isolation
等)的前提下,瀏覽器在同一層疊上下文中渲染元素的順序,從下往上堆疊。
?? 注意:
-
左上角的層疊上下文
background
/border
是指層疊上下文元素的邊框和背景顏色,是最低的層疊等級 -
inline
和inline-block
元素是相同的層疊等級,并且要高于block
和float
元素 -
之所以文字相關元素(通常是
inline
)層疊等級更高,是出于網頁設計初衷:優先保障文字可見性,避免被大面積背景或容器遮擋 -
z-index: 0
和z-index: auto
從層疊等級上看相同的,但實際上兩個屬性值有著根本的區別,具體區別可以看下表屬性值 是否創建層疊上下文 說明 z-index: auto
不會創建新的層疊上下文 元素仍處于父級的層疊上下文中 z-index: 0
會創建層疊上下文(前提是元素是定位元素) position
為relative
、absolute
、fixed
或sticky
時生效
層疊準則
知道了前面這些,我們還需要掌握:當多個元素發生重疊時,到底誰在上,誰在下?
其實只需要遵循這套判斷準則就可以判斷,為此我畫了一張流程圖:
?? 注意:
- 元素的層疊等級可以參考前面的 “層疊順序圖”,誰的層疊等級高,誰就顯示在上方
- 如果你發現調整
z-index
無效,極有可能是因為你正試圖比較不在同一層疊上下文的元素
層疊上下文特性
跟 BFC 一樣,層疊上下文也是一種獨立作用域機制,具備以下特性:
- 層疊上下文元素的層疊水平要比 普通元素(沒有創建層疊上下文的元素) 高
- 層疊上下文元素可以阻斷元素的混合模式
- 層疊上下文可以嵌套,內部層疊上下文元素及其所有子元素均受制于外部的層疊上下文
- 層疊上下文是一個獨立的渲染區域,其內部的層疊順序只在自身作用范圍內起作用
- 層疊上下文元素不會和它同級的 “兄弟元素” 或 “兄弟上下文” 互相干擾彼此內部的層疊順序
如何創建層疊上下文
前面說了那么多,那應該如何讓一個元素變成層疊上下文元素呢?
大致有如下幾種方式可以創建:
-
根元素
<html>
本身就是一個層疊上下文元素,稱為 “根層疊上下文” -
元素的
position
屬性為非static
值,并設置z-index
屬性值為非auto
值,就可以創建層疊上下文?? 注意: 在早期版本的 Firefox 和 IE 瀏覽器中,使用
position: fixed
也需要顯式設置z-index
為非auto
值才能觸發層疊上下文的創建,但在現代瀏覽器中,position: fixed
本身就能自動創建層疊上下文,即使沒有設置z-index
,這時元素的層疊等級在 “層疊順序圖” 的z-index:0/auto
一級。 -
z-index
值不為auto
的flex
子元素(父元素的display
屬性值為flex
或者inline-flex
的元素) -
元素的
opacity
值不為1
-
元素的
transform
值不為none
-
元素的
mix-blend-mode
值不為normal
-
元素的
filter
值不為none
-
元素的
isolation
值不為isolate
-
元素的
will-change
值為前面提到的任意一個屬性(例如:will-change: opacity;
) -
元素的
-webkit-overflow-scrolling
值為touch
案例演示
正所謂實踐出真知,接下來我們通過幾個典型案例,來驗證和鞏固前面講到的層疊上下文知識。
案例 1
.box {position: relative;
}.a {position: absolute;background-color: blue;z-index: 1;
}.b {position: absolute;background-color: green;z-index: 2;
}.c {position: absolute;background-color: red;z-index: 3;
}/* 其它樣式... */
<div class="box"><div class="item a">a</div><div class="item b">b</div>
</div>
<div class="box"><div class="item c">c</div>
</div>
在線預覽效果
上面這段代碼中大家可以先想一下 a
、b
、c
元素它們的層疊上下文分別是由哪個元素創建的?
答案是:
a
、b
、c
三個元素的父元素.box
雖然設置了position: relative;
,但沒有設置z-index
,所以不會產生層疊上下文,所以三個元素就都處于<html>
標簽產生的 “根層疊上下文” 中- 所以在同一層疊上下文中
c
元素的z-index
值最大,自然就出現在最前面
案例 2
.box1 {position: relative;z-index: 2;
}.box2 {position: relative;z-index: 1;
}.a {position: absolute;background-color: blue;z-index: 1;
}
.b {position: absolute;background-color: green;z-index: 2;
}
.c {position: absolute;background-color: red;z-index: 3;
}/* 其它樣式... */
<div class="box1"><div class="item a">a</div><div class="item b">b</div>
</div>
<div class="box2"><div class="item c">c</div>
</div>
在線預覽效果
上述代碼的主要結構跟案例 1 類似,只是對 a
、b
、c
三個元素的父元素增加了 z-index
,使之產生層疊上下文。
大家可以想一下,為什么明明 c
元素的 z-index
值最大,卻被比它小的 a
、b
元素給蓋住?
答案是:
-
a
、b
元素在同一個層疊上下文中,而c
元素單獨在另外一個層疊上下文中 -
此時根據層疊準則,會進行 “所屬的層疊上下文” 的層疊等級比較
a
、b
元素 “所屬的層疊上下文” 元素box1
的z-index
為2
c
元素 “所屬的層疊上下文” 元素box2
的z-index
為1
所以
c
元素被a
、b
元素蓋住 -
a
、b
元素因為是在同一個層疊上下文中,它們之間比較則是根據自身的z-index
值,b
元素的值比a
元素的大,所以b
元素蓋住了a
元素
案例 3
在過去 CSS 2.1 的時代,z-index
通常必須和定位元素一起使用才有效果,但現在 CSS3 中非定位元素也可以使用 z-index
。
.container {display: flex;
}.box1 {background-color: skyblue;width: 100px;height: 100px;margin: 20px;z-index: 2;
}.box2 {background-color: tomato;width: 150px;height: 150px;margin: 30px 0 0 -80px;z-index: 1;
}/* 其它樣式... */
<div class="container"><div class="box1">box1</div><div class="box2">box2</div>
</div>
在線預覽效果
我們可以看到 box2
元素被 box1
元素所蓋住,所以我們在使用 Flex 布局的時候,可以無需將 Flex 子元素設置為定位元素就可以使用 z-index
。
?? 注意:
- 由于
.box1
和.box2
是 flex 子元素,并且都設置了z-index
,此時它們都是層疊上下文元素,同時z-index
生效 .box
和.box2
在同一個層疊上下文中,因為父元素.container
不是層疊上下文元素,所以都處于<html>
標簽產生的 “根層疊上下文” 中
總結
- 層疊上下文是瀏覽器用來組織元素 Z 軸方向堆疊順序的一個獨立的 “視覺區域” 或 “作用域”
- 層疊水平決定了同一層疊上下文內的元素誰在上、誰在下
- 層疊上下文和層疊順序這兩個是概念,而層疊順序是指當多個元素在 Z 軸方向發生重疊時,瀏覽器決定哪些元素顯示在上、哪些元素被遮擋的一套規則
- 在遇到需要判斷多個元素重疊時,可以參考層疊準則中的流程圖來判斷誰在上,誰在下
- 創建層疊上下文的方式有很多,并非只有定位元素 +
z-index
可以創建
參考文章
- 徹底搞懂CSS層疊上下文、層疊等級、層疊順序、z-index最近,在項目中遇到一個關于CSS中元素z-index屬性的問 - 掘金
- 深入理解CSS中的層疊上下文和層疊順序 ? 張鑫旭-鑫空間-鑫生活
博客地址:https://github.com/wjw020206/blog