一文梳理z-index和層疊上下文

川南煙雨發表於2023-05-07

前言

最近參與某前端專案架構改造,發現專案中濫用z-index,設定的值有幾十種並且不統一。在對專案的z-index進行梳理和統一過程中也深入學習了一下z-index,並撰寫成文,希望也能幫助到陌生的你。

1、z-index

z-index屬性是什麼?這裡可參考MDN:

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

從上可知,z-index屬性生效的物件是定位元素(position屬性值不為static)。對於重疊的元素,如何去管理順序、解決展示衝突,一般我們都會首先想到z-index,CSS允許我們對z-index屬性設定三種值:

  • auto(自動,預設值)
  • 整數
  • inherit(繼承)

在判斷元素層疊時我們需要牢記兩個準則:

  • 誰大誰上: 在同一層疊上下文中,z-index屬性值越大,盒子在層疊中的次序就越靠近使用者的眼睛;
  • 後來居上:在同一層疊上下文中,當元素層疊順序相同(z-index一致),在DOM流中處於後面的元素會覆蓋前面的元素。
<body>
<div class="one">one</div>
<div class="two">two</div>
</body>


當都設定z-index屬性時,擁有較大z-index值的元素會掩蓋住小的那個元素:

div {
	position: relative;
}
.one {
	z-index:2;
}
.two {
	z-index:1;
}

上面的準則都是在說同一層疊上下文,那麼什麼是層疊上下文呢?層疊上下文是何時產生的呢?

2、層疊上下文

The stacking context is a three-dimensional conceptualization of HTML elements along an imaginary z-axis relative to the user, who is assumed to be facing the viewport or the webpage. HTML elements occupy this space in priority order based on element attributes. -- MDN

層疊上下文(stacking context),是HTML的一個三維的概念。頁面元素基於層疊上下文來進行順序的排列。層疊上下文在z軸上形成一個類似作用域的空間,一個層疊上下文內的所有元素不管如何設定z-index,都被限制在該層疊上下文內。需要注意:元素的層疊上下文不一定是該元素的父元素

每個網頁都有一個預設的層疊上下文,這個層疊上下文的根元素就是html元素。html標籤中的一切都被置於這個預設的層疊上下文的一個層疊層上(body)。當一個元素建立一個層疊上下文時,它的所有子元素都會受到父元素的層疊順序影響。這意味著如果一個層疊上下文位於一個最低位置的層,那麼其子元素的z-index設定得再大,它都不會出現在其他層疊上下文元素的上面。

閱讀到這裡,想必你總算知道了為什麼很多時候我們對定位元素設定z-index,無論如何設定z-index值都不能掩蓋住另一個元素的原因了。

3、層疊水平

“層疊水平”,英文稱作“stacking level”,決定了同一個層疊上下文中元素在z軸上的顯示順序。換句話說,在同一層疊上下文中的不同元素重疊時,它們的顯示順序會遵循層疊水平的規則,而z-index能夠影響元素的層疊水平。

需要再次提醒的是,在討論元素基於層疊水平進行排序時,是限制在單個層疊上下文內的。層疊水平不等於z-index屬性,所有的元素都存在層疊水平,而z-index屬性只能改變定位元素及flex盒子的孩子元素的層疊水平。

4、層疊順序

再來說說層疊順序。“層疊順序”,英文名為“stacking order”,表示元素髮生層疊時候有著特定的垂直顯示順序,這裡需要注意,上面的層疊上下文和層疊水平是概念,而這裡講到的層疊順序則是規則

在一個層疊上下文中按照層疊順序把元素分為7種層疊水平,預設的層疊順序如下圖所示:

(1)背景和邊框--形成層疊上下文元素的背景和邊框。位於層疊上下文中的最底層。

(2)負z-index--層疊上下文內z-index值為負的定位元素。

(3)塊級盒子--層疊上下文中非行內非定位元素。

(4)浮動盒子--非定位浮動元素。

(5)行內/行內快盒子 -- 層疊上下文中,inline和inline-block非定位元素。

(6)z-index:0 /auto -- 定位元素。單純考慮層疊水平,兩者表現一樣,但實際上對層疊上下文影響不一樣。

(7)正z-index值 -- 定位元素。z-index值越大,越靠近使用者。

在平時開發時,我們經常會使用(2)、(6)、(7),大部分元素的層疊水平都低於z-index為0的定位元素。

這裡順便基於上文的實驗提出一個問題,如果只給one元素設定position的relative,能否實現one元素在上面?(設定postition後,z-index:auto會自動生效)。
想必聰明的你已經有答案了,那就是可以。

為什麼inline/inline-block元素的層疊順序比浮動元素和塊元素都高呢?

諸如border/background一般為裝飾屬性,而浮動和塊元素一般用作佈局,內聯元素都是內容。網頁中展示最重要的是內容,因此內容的層疊順序比較高,當發生層疊時,重要的文字和圖片等內容優先暴露在螢幕上。

5、建立層疊上下文

層疊上下文元素有以下幾個特性:

  • 層疊上下文可以巢狀,內部層疊上下文及其所有子元素均受制於外部的層疊上下文。
  • 每個層疊上下文和兄弟元素獨立,當層疊變化或渲染的時候,只需考慮後代元素。
  • 每個層疊上下文時自成體系的,當元素髮生層疊的時候,整個元素被認為是在父層疊上下文的層疊順序中。

那麼如何才能建立一個層疊上下文呢?根據MDN,當滿足以下任一條件的元素就會建立層疊上下文:

  • 文件根元素(html),生成根層疊上下文,包裹在所有元素的最外層。
  • position值為absolute或者relative並且z-index不為auto的元素
  • position值為fixed或sticky的元素。
  • z-index不為auto的所有flex容器的子元素。
  • z-index不為auto的所有grid容器的子元素。
  • opacity 屬性值小於 1 的元素.
  • mix-blend-mode 屬性值不為 normal 的元素.
  • 任一屬性值不為 none 的元素: transform |filter | backdrop-filter | perspective |clip-path |mask | mask-image | mask-border。
  • isolation 屬性值為 isolate 的元素。
  • will-change 值設定了任一屬性而該屬性在 non-initial 值時會建立層疊上下文的元素。
  • -webkit-overflow-scrolling屬性值為touch的元素。
  • contain 屬性值為 layout、paint 或包含它們其中之一的合成值(比如 contain: strict、contain: content)的元素。

在層疊上下文中,子元素同樣也按照上面解釋的規則進行層疊。重要的是,其子級層疊上下文的 z-index 值只在父級中才有意義。子級層疊上下文被自動視為父級層疊上下文的一個獨立單元。

接下來透過一個示例說明flex屬性對層疊上下文的影響。程式碼中,two元素與圖片有相同層疊上下文,z-index為負值。

<div class="one">
	<div class="two" style={{backgroundColor: "blue", zIndex: "1"}}>  {/* 普通元素,z-index不生效 */}
		 <img src='./imgs/38558887.jpeg' style={{position: "relative", zIndex: "-1"}} />
	</div>
</div>

two塊元素和img元素有相同的層疊上下文,z-index值為負的圖片在塊元素之下。現在,我們給one元素設定display屬性值為flex:

<div class="one" style={{display: "flex"}}>
	<div class="two" style={{backgroundColor: "blue", zIndex: "1"}}> 
		<img src={img} style={{position: "relative", zIndex: "-1", height:"60px"}} />
	 </div>
 </div>

當設定父元素flex屬性後,圖片跑到上方來了。這是因為設定flex屬性後two元素建立了一個層疊上下文,包裹圖片成為圖片的父層疊上下文,背景的層疊順序低於負z-index值的img元素。

小結

本文從z-index出發,介紹了層疊上下文、層疊順序,並簡要闡述了元素在什麼條件會產生層疊上下文,同時舉例說明了flex屬性如何影響層疊水平。希望能夠幫助你理解層疊上下文和層疊水平,由於作者水平有限,難免行文有誤,如有不當請不吝指教,謝謝!

ps: 這裡安利兩個看z-index和層疊上下文的chrom外掛:z-index | z-context

相關文章