动态

详情 返回 返回

CSS專題之層疊上下文 - 动态 详情

前言

石匠敲擊石頭的第 15 次

在平常開發的時候,有時候會遇到使用 z-index 調整元素層級沒有效果的情況,究其原因還是因為對層疊上下文不太瞭解,看了網上很多前輩的文章,決定打算寫一篇文章來梳理一下,如果哪裏寫的有問題歡迎指出,不勝感激。

什麼是層疊上下文

層疊上下文(Stacking Context)是 HTML 中的三維概念,它決定了元素在 Z 軸(垂直於電腦屏幕的方向) 上的顯示順序。簡單來説,層疊上下文是瀏覽器用來組織元素 Z 軸方向堆疊順序的一個獨立的 “視覺區域” 或 “作用域”

image-20250525130425840.png

層疊上下文的概念有點類似於塊級格式化上下文(BFC),都是在特定條件下由元素創建的獨立作用區域,並且區域內遵循各自的規則,但它作用在視覺層級(z-index)而非佈局流

什麼是層疊水平

在一個層疊上下文中,層疊水平(Stacking Level,也叫層疊等級) 用於確定其子元素在 Z 軸方向上的顯示優先級,簡單來説,層疊水平決定了同一層疊上下文內的元素誰在上、誰在下

⚠️ 注意:

  • 元素的層疊水平是由其所屬的層疊上下文來決定的,也就是説層疊水平的比較只有在同一個層疊上下文中才有意義,所以不同層疊上下文之間的元素不會互相影響
  • 某些情況下(Flex 子元素和定位元素) z-index 確實可以影響元素的層疊水平,但層疊水平不等於 z-index所有元素都有層疊水平

什麼是層疊順序

層疊上下文和層疊順序這兩個終究只是抽象的概念,但元素具體是按照什麼規則層疊的,這就不得不提層疊順序(Stacking Order)

簡單來説,層疊順序就是指當多個元素在 Z 軸方向發生重疊時,瀏覽器決定哪些元素顯示在上、哪些元素被遮擋的一套規則。

image-20250527073255399.png

上圖展示了在不考慮 CSS3 新特性(如 flexgridisolation 等)的前提下,瀏覽器在同一層疊上下文中渲染元素的順序,從下往上堆疊。

⚠️ 注意:

  • 左上角的層疊上下文 background / border 是指層疊上下文元素的邊框和背景顏色,是最低的層疊等級
  • inlineinline-block 元素是相同的層疊等級,並且要高於 blockfloat 元素
  • 之所以文字相關元素(通常是 inline)層疊等級更高,是出於網頁設計初衷:優先保障文字可見性,避免被大面積背景或容器遮擋
  • z-index: 0z-index: auto 從層疊等級上看相同的,但實際上兩個屬性值有着根本的區別,具體區別可以看下錶

    屬性值 是否創建層疊上下文 説明
    z-index: auto 不會創建新的層疊上下文 元素仍處於父級的層疊上下文中
    z-index: 0 會創建層疊上下文(前提是元素是定位元素) positionrelativeabsolutefixedsticky 時生效

層疊準則

知道了前面這些,我們還需要掌握:當多個元素髮生重疊時,到底誰在上,誰在下

其實只需要遵循這套判斷準則就可以判斷,為此我畫了一張流程圖:

image-20250528083339074.png

⚠️ 注意:

  • 元素的層疊等級可以參考前面的 “層疊順序圖”,誰的層疊等級高,誰就顯示在上方
  • 如果你發現調整 z-index 無效,極有可能是因為你正試圖比較不在同一層疊上下文的元素

層疊上下文特性

跟 BFC 一樣,層疊上下文也是一種獨立作用域機制,具備以下特性:

  • 層疊上下文元素的層疊水平要比 普通元素(沒有創建層疊上下文的元素)
  • 層疊上下文元素可以阻斷元素的混合模式
  • 層疊上下文可以嵌套,內部層疊上下文元素及其所有子元素均受制於外部的層疊上下文
  • 層疊上下文是一個獨立的渲染區域,其內部的層疊順序只在自身作用範圍內起作用
  • 層疊上下文元素不會和它同級的 “兄弟元素” 或 “兄弟上下文” 互相干擾彼此內部的層疊順序

如何創建層疊上下文

前面説了那麼多,那應該如何讓一個元素變成層疊上下文元素呢?

大致有如下幾種方式可以創建:

  • 根元素 <html> 本身就是一個層疊上下文元素,稱為 “根層疊上下文”
  • 元素的 position 屬性為static 值,並設置 z-index 屬性值為非 auto,就可以創建層疊上下文

    ⚠️ 注意: 在早期版本的 Firefox 和 IE 瀏覽器中,使用 position: fixed 也需要顯式設置 z-index 為非 auto 值才能觸發層疊上下文的創建,但在現代瀏覽器中,position: fixed 本身就能自動創建層疊上下文,即使沒有設置 z-index,這時元素的層疊等級在 “層疊順序圖” 的 z-index:0/auto 一級。

    image-20250528090127942.png

  • z-index 值不為 autoflex 子元素(父元素的 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>

image-20250529074117642.png

在線預覽效果

上面這段代碼中大家可以先想一下 abc 元素它們的層疊上下文分別是由哪個元素創建的?

答案是:

  • abc 三個元素的父元素 .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>

image-20250529080003130.png

在線預覽效果

上述代碼的主要結構跟案例 1 類似,只是對 abc 三個元素的父元素增加了 z-index,使之產生層疊上下文。

大家可以想一下,為什麼明明 c 元素的 z-index 值最大,卻被比它小的 ab 元素給蓋住?

答案是:

  • ab 元素在同一個層疊上下文中,而 c 元素單獨在另外一個層疊上下文中
  • 此時根據層疊準則,會進行 “所屬的層疊上下文” 的層疊等級比較

    • ab 元素 “所屬的層疊上下文” 元素 box1z-index2
    • c 元素 “所屬的層疊上下文” 元素 box2z-index1

    所以 c 元素被 ab 元素蓋住

  • ab 元素因為是在同一個層疊上下文中,它們之間比較則是根據自身的 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>

image-20250531092208027.png

在線預覽效果

我們可以看到 box2 元素被 box1 元素所蓋住,所以我們在使用 Flex 佈局的時候,可以無需將 Flex 子元素設置為定位元素就可以使用 z-index

⚠️ 注意:

  • 由於 .box1.box2flex 子元素,並且都設置了 z-index此時它們都是層疊上下文元素,同時 z-index 生效
  • .box.box2 在同一個層疊上下文中,因為父元素 .container 不是層疊上下文元素,所以都處於 <html> 標籤產生的 “根層疊上下文”

總結

  • 層疊上下文是瀏覽器用來組織元素 Z 軸方向堆疊順序的一個獨立的 “視覺區域” 或 “作用域”
  • 層疊水平決定了同一層疊上下文內的元素誰在上、誰在下
  • 層疊上下文和層疊順序這兩個是概念,而層疊順序是指當多個元素在 Z 軸方向發生重疊時,瀏覽器決定哪些元素顯示在上、哪些元素被遮擋的一套規則
  • 在遇到需要判斷多個元素重疊時,可以參考層疊準則中的流程圖來判斷誰在上,誰在下
  • 創建層疊上下文的方式有很多,並非只有定位元素 + z-index 可以創建

參考文章

  • 徹底搞懂CSS層疊上下文、層疊等級、層疊順序、z-index最近,在項目中遇到一個關於CSS中元素z-index屬性的問 - 掘金
  • 深入理解CSS中的層疊上下文和層疊順序 « 張鑫旭-鑫空間-鑫生活

博客地址:https://github.com/wjw020206/blog

user avatar cyzf 头像 alibabawenyujishu 头像 haoqidewukong 头像 smalike 头像 jingdongkeji 头像 dirackeeko 头像 dawanzi_6278b06ec111c 头像 razyliang 头像 huajianketang 头像 banana_god 头像 huichangkudelingdai 头像 talkcss 头像
点赞 187 用户, 点赞了这篇动态!
点赞

Add a new 评论

Some HTML is okay.