為什麼我寫的z-index不生效?

2022-10-09 12:00:40

前言

相信大家在工作中都遇到過這樣一些奇怪的問題:

1.為什麼我寫的z-index沒有生效?

2.為什麼z-index大的元素卻沒有蓋住z-index小的元素?

3.如何讓父元素蓋住子元素呢?

以上這些問題都跟CSS層疊上下文有關,帶著上面這些問題我們一起來了解一下什麼是CSS層疊上下文,以及這些奇怪現象背後的原理!

如果這篇文章有幫助到你,❤️關注+點贊❤️鼓勵一下作者,文章公眾號首發,關注 前端南玖 第一時間獲取最新文章~

什麼是CSS層疊上下文?

層疊上下文是HTML元素的三維概念,這些HTML元素在一條假想的相對於面向(電腦螢幕的)視窗或者網頁的使用者的z軸上延伸,HTML元素依據其自身屬性按照優先順序順序佔用層疊上下文的空間。

z軸

在CSS2.1規範中,每個盒模型的位置是三維的,分別是平面畫布上的X軸Y軸以及表示層疊的Z軸。一般情況下,元素在頁面上沿X軸Y軸平鋪,我們察覺不到它們在Z軸上的層疊關係。而一旦元素髮生堆疊,這時就能發現某個元素可能覆蓋了另一個元素或者被另一個元素覆蓋。

我們可以這樣來理解:

  • 層疊上下文是HTML元素的三維概念,可以想象為一條垂直於視窗的z軸
  • 當元素建立了重疊上下文時,這個元素就有了一個z軸
  • 如果內部的子元素髮生重疊,會依據自身屬性優先順序順序佔用z軸(重疊上下文)上的空間
  • 優先順序最大的元素排在最上面,離使用者也最近

如何產生層疊上下文?

瞭解了層疊上下文,我們還要知道層疊上下文是如何產生的。

一般來講有3種方法:

  • html中的根元素<html></html>本身就是層疊上下文,成為根層疊上下文

  • position屬性為static值並設定z-index屬性為具體數值

  • 一些CSS3屬性也能產生層疊上下文

    • 一個 flex 元素(flex item),且 z-index 值不為 「auto」,也就是父元素 display: flex|inline-flex
    • 元素的 opacity 屬性值小於 1
    • 元素的 transform 屬性值不為 「none」
    • 元素的 mix-blend-mode 屬性值不為 「normal」
    • 元素的 isolation 屬性被設定為 「isolate」
    • 在 mobile WebKit 和 Chrome 22+ 核心的瀏覽器中,position: fixed 總是建立一個新的層疊上下文, 即使 z-index 的值是 「auto」
    • 在 will-change 中指定了任意 CSS 屬性,即便你沒有定義該元素的這些屬性
    • 元素的 -webkit-overflow-scrolling 屬性被設定 「touch」

層疊等級與層疊順序

層疊等級

層疊等級(stacking level,叫「層疊級別」/「層疊水平」也行),它決定了同一個層疊上下文中元素在z軸上的顯示順序(層疊順序) ,也就是說普通元素的層疊水平優先由層疊上下文決定。

層疊順序

「層疊順序」英文稱作」stacking order」. 表示元素髮生層疊時候有著特定的垂直顯示順序,注意,這裡跟上面兩個不一樣,上面的層疊上下文和層疊水平是概念,而這裡的層疊順序是規則

從上面產生層疊上下文的方法,我們可以分為CSS2.1與CSS3兩類,在CSS3出來之前,相信大家都看過下面這張圖:

看到這張圖,相信大家最有疑問的是行內元素的層疊順序要高於塊級元素與浮動元素

OK,有疑問就動手實踐一遍,看看是不是真是這樣:

<style>
  div {
    width: 100px;
    height: 100px;
    border: 1px solid saddlebrown;
  }
  .box1 {
    position: relative;
    z-index: -1;
    background: violet;
  }
  .box2 {
    margin-top: -50px;
    margin-left: 50px;
    background: salmon;
  }
  .box3 {
    float: left;
    margin-top: -50px;
    margin-left: 100px;
    background: wheat;
  }
  .box4 {
    display: inline-block;
    background: greenyellow;
    margin-left: -50px;
  }
  .box5 {
    position: relative;
    z-index:0;
    left: 200px;
    top: -50px;
    background: palevioletred;
  }
  .box6 {
    position: relative;
    z-index: 1;
    left: 250px;
    top: -100px;
    background: gold
  }
</style>
</head>
<body>
  <div class="box1">1定位z-index<0</div>
  <div class="box2">2塊級元素</div>
  <div class="box3">3浮動</div>
  <div class="box4">4行內元素</div>
  <div class="box5">5定位z-index=0</div>
  <div class="box6">6定位z-index>0</div>
</body>

行內元素的層疊順序為什麼要高於塊級元素與浮動元素

這個理解起來其實很簡單,像border/background屬於裝飾元素的屬性,浮動和塊級元素一般用來頁面佈局,而內聯元素一般都是文字內容,並且網頁設計之初最重要的就是文字內容,所以在發生層疊時會優先顯示文字內容,保證其不被覆蓋。

層疊順序規則

  • 誰大誰上:當具有明顯的層疊水平標示的時候,比如說z-index值,在同一個層疊上下文領域,層疊水平值大的覆蓋小的
  • 後來居上:當元素的層疊水平一致、層疊順序相同的時候,在DOM流中處於後面的元素會覆蓋前面的元素。

z-index

z-index 屬性設定了一個定位元素及其後代元素或 flex 專案的 z-order。當元素之間重疊的時候,z-index 較大的元素會覆蓋較小的元素在上層進行顯示。

屬性值

  • auto: 預設值,當前值與父級相同
  • <integer>: 整型數位

基本特性

  1. z-index 屬性允許為負值。
  2. z-index 屬性支援 CSS3 animation 動畫。
  3. 在 CSS 2.1 的時候,需要配合 position 屬性且值不為 static 時使用。

解惑

瞭解完上面這些內容,現在我們再來看一看前文提到的一些問題

1.為什麼我寫的z-index沒有生效?

這個很簡單,因為它單獨使用時不生效,一定要配合定位屬性一起,即只對指定了position屬性的元素生效——只要不是預設值static,其他的absolute、relative、fixed都可以使z-index生效。(在CSS3之後,彈性元素的子元素也可以生效)

2.為什麼z-index大的元素卻沒有蓋住z-index小的元素?

這裡我們可以來看一個有趣的現象

<style>
  .box1 {
    width: 200px;
    height: 100px;
    background: red;
  }
  .box2 {
    width: 100px;
    height: 200px;
    background: greenyellow;
  }
</style>
<div style="position:relative; z-index:auto;">
  <div style="position:absolute; z-index:2;" class="box1">box1--z-index=2</div>
</div>
​
<div style="position:relative; z-index:auto;">
  <div style="position:relative; z-index:1;" class="box2">box2--z-index=1</div>
</div>


這麼看還挺正常的,z-index值大的在z-index值小的上方。接下來我們稍微改一改,你就能看到奇怪的現象了

<div style="position:relative; z-index:0;">
  <div style="position:absolute; z-index:2;" class="box1">box1--z-index=2</div>
</div>
​
<div style="position:relative; z-index:0;">
  <div style="position:relative; z-index:1;" class="box2">box2--z-index=1</div>
</div>

這裡我們只是把它們父元素的z-index屬性從auto改成了0,兩種情況的表現卻截然相反。

產生這種現象的原因我們也能夠從上面的理論中找到答案:position屬性為static值並設定z-index屬性為具體數值才能產生層疊上下文

當z-index為auto時,是一個普通元素,兩個box層比較不受父級的影響,按照規則誰大誰上,於是z-index為2的box覆蓋值為1的box; 當z-index為0時,會建立一個層疊上下文,此時的層疊規則就發生了變化。層疊上下文特性裡最後一條規則,每個層疊上下文都是獨立的。兩個box的層疊順序比較變成了優先比較其父級層疊上下文元素的層疊順序。由於兩者z-index都是0,所以,遵循層疊規則後來居上,根據在DOM出現的先後順序決定誰在上面,於是,位於後面的box2覆蓋box1。此時box元素上的z-index是沒有任何意義的。

3.如何讓父元素蓋住子元素?

這裡很多人是不是認為直接讓父元素的z-index大於子元素的z-index不就好了,可事實真是如此嗎?

<style>
  .outer {
    position: relative;
    width: 100px;
    height: 200px;
    background: salmon;
    z-index: 3;
  }
  .inner {
    position: relative;
    width: 50px;
    height: 200px;
    background: cadetblue;
    z-index: 1;
  }
</style>
<div class="outer">
  父元素
  <div class="inner">子元素</div>
</div>

有人是不是又有疑惑了?

我們這樣來理解,父元素定位+z-index為數值,所以它產生了一個層疊上下文,此時子元素無論怎麼設定z-index都不可能在父元素的下方。唯一可以實現的方法是將子元素的z-index設為負值,而父元素只要不產生層疊上下文就可以了。

<style>
  .outer {
    position: relative;
    width: 100px;
    height: 200px;
    background: salmon;
    /**z-index: 3;**/
  }
  .inner {
    position: relative;
    width: 50px;
    height: 200px;
    background: cadetblue;
    z-index: -1;
  }
</style>
<div class="outer">
  父元素
  <div class="inner">子元素</div>
</div>

總結

  • 層疊上下文的層疊水平要比普通元素高
  • 層疊上下文可以巢狀,內部層疊上下文及其所有子元素均受制於外部的層疊上下文
  • 每個層疊上下文不會影響它的兄弟元素,當進行層疊變化或渲染的時,只和該元素的後代元素有關
  • 每個層疊上下文是獨立的,當元素髮生層疊的時,它的層疊順序依賴在父層疊上下文的層疊順序中

其餘規則看上面層疊順序的圖即可。

原文首發地址點這裡,歡迎大家關注公眾號 「前端南玖」,如果你想進前端交流群一起學習,請點這裡

我是南玖,我們下期見!!!