前言
石匠敲擊石頭的第 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為2c元素 “所屬的層疊上下文” 元素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