完全搞懂CSS層疊上下文、層疊等級、層疊順序、z-index

前言

最近,在項目中遇到一個關於CSS中元素z-index屬性的問題,具體問題不太好描述,總結起來就是當給元素和父元素色設置position屬性和z-index相關屬性後,頁面上渲染的元素層級結果和我預想的不同。根據本身以前的理解,也沒找到一個合理的解釋。我知道,確定是我對相關屬性的細節理解存在問題,因此結合官方文檔和在網上各類蒐集整理,明白了其中的緣由。寫下這篇文章,和你們分享有關CSS中層疊上下文層疊等級層疊順序以及z-index相關的一整套技術細節。css

若是存在什麼錯誤或重要遺漏或者有什麼疑問,歡迎留言指正、討論!感謝!html

本文已同步至個人我的主頁。更多內容,歡迎訪問個人GitHub主頁,謝謝關注和支持!git

一個「片面」的理解

以往,因爲本身使用z-index的頻率不大,因此對這個CSS屬性存在比較片面的認識。一直認爲z-index就是用來描述定義一個元素在屏幕Z軸上的堆疊順序。z-index值越大在Z軸上就越靠上,也就是離屏幕觀察者越近。最後才發現這個認識存在很大的問題:github

  1. 首先,z-index屬性值並非在任何元素上都有效果。它僅在定位元素(定義了position屬性,且屬性值爲非static值的元素)上有效果。
  2. 判斷元素在Z軸上的堆疊順序,不只僅是直接比較兩個元素的z-index值的大小,這個堆疊順序實際由元素的層疊上下文層疊等級共同決定。

要想徹底理解一個東西,首先要明白它是什麼,也就是它的定義。咱們先看看上面提到的層疊上下文層疊等級層疊順序都是什麼?定義又太過抽象,後面會再用一個具象的比喻來讓你完全明白它們究竟是什麼,有什麼聯繫。web

什麼是「層疊上下文」

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

若是一個元素含有層疊上下文,(也就是說它是層疊上下文元素),咱們能夠理解爲這個元素在Z軸上就「高人一等」,最終表現就是它離屏幕觀察者更近。wordpress

具象的比喻:你能夠把層疊上下文元素理解爲理解爲該元素當了官,而其餘非層疊上下文元素則能夠理解爲普通羣衆。凡是「當了官的元素」就比普通元素等級要高,也就是說元素在Z軸上更靠上,更靠近觀察者。佈局

什麼是「層疊等級」

那麼,層疊等級指的又是什麼?層疊等級(stacking level,叫「層疊級別」/「層疊水平」也行)學習

  • 在同一個層疊上下文中,它描述定義的是該層疊上下文中的層疊上下文元素在Z軸上的上下順序。
  • 在其餘普通元素中,它描述定義的是這些普通元素在Z軸上的上下順序。

說到這,可能不少人疑問了,不論在層疊上下文中仍是在普通元素中,層疊等級都表示元素在Z軸上的上下順序,那就直接說它描述定義了全部元素在Z軸上的上下順序就OK啊!爲何要分開描述?測試

爲了說明緣由,先舉個栗子:

具象的比喻:咱們以前說到,處於層疊上下文中的元素,就像是元素當了官,等級天然比普通元素高。再想象一下,假設一個官員A是個省級領導,他下屬有一個祕書a-1,家裏有一個保姆a-2。另外一個官員B是一個縣級領導,他下屬有一個祕書b-1,家裏有一個保姆b-2。a-1和b-1雖然都是祕書,可是你想一個省級領導的祕書和一個縣級領導的祕書之間有可比性麼?甚至保姆a-2都要比祕書b-1的等級高得多。誰大誰小,誰高誰低一目瞭然,因此根本沒有比較的意義。只有在A下屬的a-一、a-2以及B下屬的b-一、b-2中相互比較大小高低纔有意義。

再類比回「層疊上下文」和「層疊等級」,就得出一個結論:

  1. 普通元素的層疊等級優先由其所在的層疊上下文決定。
  2. 層疊等級的比較只有在當前層疊上下文元素中才有意義。不一樣層疊上下文中比較層疊等級是沒有意義的。

如何產生「層疊上下文」

前面說了那麼多,知道了「層疊上下文」和「層疊等級」,其中還有一個最關鍵的問題:到底如何產生層疊上下文呢?如何讓一個元素變成層疊上下文元素呢?

其實,層疊上下文也基本上是有一些特定的CSS屬性建立的,通常有3種方法:

  1. HTML中的根元素<html></html>自己j就具備層疊上下文,稱爲「根層疊上下文」。
  2. 普通元素設置position屬性爲static值並設置z-index屬性爲具體數值,產生層疊上下文。
  3. CSS3中的新屬性也能夠產生層疊上下文。

至此,終於能夠上代碼了,咱們用代碼說話,來驗證上面的結論:

栗子1: 有兩個div,p.a、p.b被包裹在一個div裏,p.c被包裹在另外一個盒子裏,只爲.a、.b、.c設置positionz-index屬性

<style>
  div {  
    position: relative;  
    width: 100px;  
    height: 100px;  
  }  
  p {  
    position: absolute;  
    font-size: 20px;  
    width: 100px;  
    height: 100px;  
  }  
  .a {  
    background-color: blue;  
    z-index: 1;  
  }  
  .b {  
    background-color: green;  
    z-index: 2;  
    top: 20px;  
    left: 20px;  
  }  
  .c {  
    background-color: red;  
    z-index: 3;  
    top: -20px;  
    left: 40px;  
  }
</style>

<body>  
  <div>  
    <p class="a">a</p>  
    <p class="b">b</p>  
  </div> 

  <div>  
    <p class="c">c</p>  
  </div>  
</body> 
複製代碼

效果:

由於p.a、p.b、p.c三個的父元素div都沒有設置 z-index,因此不會產生層疊上下文,因此.a、.b、.c都處於由 <html></html>標籤產生的「根層疊上下文」中,屬於同一個層疊上下文,此時誰的 z-index值大,誰在上面。

栗子2: 有兩個div,p.a、p.b被包裹在一個div裏,p.c被包裹在另外一個盒子裏,同時爲兩個div和.a、.b、.c設置positionz-index屬性

<style>
  div {
    width: 100px;
    height: 100px;
    position: relative;
  }
  .box1 {
    z-index: 2;
  }
  .box2 {
    z-index: 1;
  }
  p {
    position: absolute;
    font-size: 20px;
    width: 100px;
    height: 100px;
  }
  .a {
    background-color: blue;
    z-index: 100;
  }
  .b {
    background-color: green;
    top: 20px;
    left: 20px;
    z-index: 200;
  }
  .c {
    background-color: red;
    top: -20px;
    left: 40px;
    z-index: 9999;
  }
</style>

<body>
  <div class="box1">
    <p class="a">a</p>
    <p class="b">b</p>
  </div>

  <div class="box2">
    <p class="c">c</p>
  </div>
</body>
複製代碼

效果:

咱們發下,雖然 p.c元素的 z-index值爲9999,遠大於 p.ap.bz-index值,可是因爲 p.ap.b的父元素 div.box1產生的層疊上下文的 z-index的值爲2, p.c的父元素 div.box2所產生的層疊上下文的 z-index值爲1,因此 p.c永遠在 p.ap.b下面。

同時,若是咱們只更改p.ap.bz-index值,因爲這兩個元素都在父元素div.box1產生的層疊上下文中,因此,誰的z-index值大,誰在上面。

什麼是「層疊順序」

說完「層疊上下文」和「層疊等級」,咱們再來講說「層疊順序」。「層疊順序」(stacking order)表示元素髮生層疊時按照特定的順序規則在Z軸上垂直顯示。因而可知,前面所說的「層疊上下文」和「層疊等級」是一種概念,而這裏的「層疊順序」是一種規則。

在不考慮CSS3的狀況下,當元素髮生層疊時,層疊順訊遵循上面途中的規則。 這裏值得注意的是:

  1. 左上角"層疊上下文background/border"指的是層疊上下文元素的背景和邊框。
  2. inline/inline-block元素的層疊順序要高於block(塊級)/float(浮動)元素。
  3. 單純考慮層疊順序,z-index: autoz-index: 0在同一層級,但這兩個屬性值自己是有根本區別的。

對於上面第2條,爲何inline/inline-block元素的層疊順序要高於block(塊級)/float(浮動)元素?這個你們能夠思考一下! 其實很簡單,像border/background屬於裝飾元素的屬性,浮動和塊級元素通常用來頁面佈局,而網頁設計之初最重要的就是文字內容,因此在發生層疊時會優先顯示文字內容,保證其不被覆蓋。

你要的「套路」

上面說了那麼多,可能你仍是有點懵。這麼多概念規則,來點最實際的,有沒有一個「套路」當遇到元素層疊時,能很清晰地判斷出他們誰在上誰在下呢?答案是——確定有啊!

一、首先先看要比較的兩個元素是否處於同一個層疊上下文中:       1.1若是是,誰的層疊等級大,誰在上面(怎麼判斷層疊等級大小呢?——看「層疊順序」圖)。       1.2若是兩個元素不在統一層疊上下文中,請先比較他們所處的層疊上下文的層疊等級。 二、當兩個元素層疊等級相同、層疊順序相同時,在DOM結構中後面的元素層疊等級在前面元素之上。

光說不練假把式

對於技術學習,代碼展現是最直觀最易懂的方式之一。話很少說,直接上代碼,咱們經過如下幾個「栗子」,來進一步驗證掌握上面的結論。

栗子3:

<style>
  .box1, .box2 {
    position: relative;
    z-index: auto;
  }
  .child1 {
    width: 200px;
    height: 100px;
    background: #168bf5;
    position: absolute;
    top: 0;
    left: 0;
    z-index: 2;
  }
  .child2 {
    width: 100px;
    height: 200px;
    background: #32c292;
    position: absolute;
    top: 0;
    left: 0;
    z-index: 1;
  }
</style>
</head>

<body>
  <div class="box1">
    <div class="child1"></div>
  </div>

  <div class="box2">
    <div class="child2"></div>
  </div>
</body>
複製代碼

效果:

說明: .box1/.box2雖然設置了 position: relative,可是 z-index: auto的狀況下,這兩個 div仍是普通元素,並無產生層疊上下文。因此, child1/.child2屬於 <html></html>元素的「根層疊上下文」中,此時, 誰的z-index值大,誰在上面

栗子4:

對於栗子1中的CSS代碼,咱們只把.box1/.box2z-index屬性值改成數值0,其他不變。

.box1, .box2 {
  position: relative;
  z-index: 0;
}
...

複製代碼

效果:

說明: 此時,咱們發現,僅僅修改了 .box1/.box2z-index屬性值改成 數值0,最終結果徹底相反。這時 .child2覆蓋在了 .child1上面。緣由是什麼呢?很簡單:由於設置 z-index: 0後, .box1/.box2產生了各自的層疊上下文,這時候要比較 .child1/.child2的層疊關係徹底由父元素 .box1/.box2的層疊關係決定。可是 .box1/.box2z-index值都爲 0,都是塊級元素(因此它們的層疊等級,層疊順序是相同的),這種狀況下,在 DOM結構中 後面的覆蓋前面的,因此 .child2就在上面。

CSS3中的屬性對層疊上下文的影響

CSS3中出現了不少新屬性,其中一些屬性對層疊上下文也產生了很大的影響。以下:

  1. 父元素的display屬性值爲flex|inline-flex,子元素z-index屬性值不爲auto的時候,子元素爲層疊上下文元素;
  2. 元素的opacity屬性值不是1
  3. 元素的transform屬性值不是none
  4. 元素mix-blend-mode屬性值不是normal`;
  5. 元素的filter屬性值不是none
  6. 元素的isolation屬性值是isolate
  7. will-change指定的屬性值爲上面任意一個;
  8. 元素的-webkit-overflow-scrolling屬性值設置爲touch

CSS3中,元素屬性知足以上條件之一,就會產生層疊上下文。咱們用第1條來作一個簡單的解釋說明。

栗子5:

<style>
  .box {
  }
  .parent {
    width: 200px;
    height: 100px;
    background: #168bf5;
    /* 雖然設置了z-index,可是沒有設置position,z-index無效,.parent仍是普通元素,沒有產生層疊上下文 */
    z-index: 1;
  }
  .child {
    width: 100px;
    height: 200px;
    background: #32d19c;
    position: relative;
    z-index: -1;
  }
</style>
</head>

<body>
  <div class="box">
    <div class="parent">
      parent
      <div class="child">child</div>
    </div>
  </div>
</body>
複製代碼

效果:

說明: 咱們發現, .child.parent覆蓋了。按照「套路」來分析一下: 雖然 .parent設置了 z-index屬性值,可是沒有設置 position屬性, z-index無效,因此沒有產生層疊上下文, .parent仍是普通的塊級元素。此時,在層疊順序規則中, z-index值小於 0.child會被普通的 block塊級元素 .parent覆蓋。

栗子6:

對於上面的栗子,咱們只修改.box的屬性,設置display: flex,其他屬性和DOM結構不變。

.box {
  display: flex;
}
複製代碼

效果:

說明: 當給 .box設置 display: flex時, .parent就變成層疊上下文元素,根據層疊順序規則,層疊上下文元素的 background/border的層疊等級小於 z-index值小於 0的元素的層疊等級,因此 z-index值爲 -1.child.parent上面。

小測試

下面的代碼,我會把最終頁面渲染的結果放在代碼以後,有興趣的「童鞋」能夠分析一下,各個元素的層疊等級,最後來肯定這些元素哪一個在上哪一個在下。

<style>
  .parent {
    width: 100px;
    height: 200px;
    background: #168bf5;
    position: absolute;
    top: 0;
    left: 0;
    z-index: 0;
  }
  .child1 {
    width: 100px;
    height: 200px;
    background: #32d19c;
    position: absolute;
    top: 20px;
    left: 20px;
    z-index: 1;
  }
  .child2 {
    width: 100px;
    height: 200px;
    background: #e4c950;
    position: absolute;
    top: 40px;
    left: 40px;
    z-index: -1;
  }
  .child2-1 {
    width: 100px;
    height: 200px;
    background: #e45050;
    position: absolute;
    top: 60px;
    left: 60px;
    z-index: 9999;
  }
  .child2-2 {
    width: 100px;
    height: 200px;
    background: #db68a7;
    position: absolute;
    top: 80px;
    left: 40px;
    z-index: -9999;
  }
</style>
</head>

<body>
  <div class="parent">
    parent
    <div class="child1">child1</div>
    <div class="child2">
      child2
      <div class="child2-1">child2-1</div>
      <div class="child2-2">child2-2</div>
    </div>
  </div>
</body>
複製代碼

效果:

參考文章

以上的內容有一部份內容參考瞭如下兩位大神的博客,寫的很好,也很清晰,推薦你們看一看,你會對相關知識點掌握的更清晰。

  1. 張鑫旭-《深刻理解CSS中的層疊上下文和層疊順序》
  2. AMInInsist-《CSS 中的z-index屬性》

推薦文章

下面的文章連接是我在學習實踐過程當中看到的一些以爲比較好的文章,有興趣霍有須要的朋友能夠參考參考,但願能夠幫你完全弄清文章中涉及的這些問題。

  1. lijinxieyang-《層疊上下文【stacking context】與層疊順序【stacking order】》
  2. w3help.org - 《KB013: 分層的顯示( Layered presentation )》
  3. MDN-《The stacking context》
  4. 尚-《css之層疊上下文和層疊順序》
相關文章
相關標籤/搜索