CSS 中重要的層疊概念
最近在專案的過程中遇到了一個問題,menu-bar希望始終顯示在最上面,而在之後的元素都顯示在它之下,當時設定了 z-index 也沒有效果,不知道什麼原因,因此找了一下css有關層疊方面的資料,解決了這個問題,這裡記錄一下~
螢幕是一個二維平面,然而HTML元素卻是排列在三維座標系中,x為水平位置,y為垂直位置,z為螢幕由內向外方向的位置,我們在看螢幕的時候是沿著z軸方向從外向內的;由此,元素在使用者視角就形成了層疊的關係,某個元素可能覆蓋了其他元素也可能被其他元素覆蓋;

那麼這裡有幾個重要的概念: 層疊上下文 (堆疊上下文, Stacking Context)、 層疊等級 (層疊水平, Stacking Level)、 層疊順序 (層疊次序, 堆疊順序, Stacking Order)、 z-index
宣告:
- 以下定位元素指的是
position: absolute|fixed|relative|sticky
- 以下非定位元素指的是
position: initial|static
- 關於層疊上下文還有一個類似的概念: 塊級格式化上下文 (BFC, Block Formatting Context),可以參考一下CSS 中重要的BFC,其中還介紹了一些文件流的內容;
- 本文蠻長的,但是如果你有勇氣看完,那應該對層疊有關概念就基本掌握了 (~o ̄▽ ̄)~
1. 層疊上下文 (Stacking Context)
層疊上下文(堆疊上下文, Stacking Context),是HTML中一個三維的概念。在CSS2.1規範中,每個元素的位置是三維的,當元素髮生層疊,這時它可能覆蓋了其他元素或者被其他元素覆蓋;排在z軸越靠上的位置,距離螢幕觀察者越近
文章 ofollow,noindex"><關於z-index 那些你不知道的事> 有一個很好的比喻,這裡引用一下;
可以想象一張桌子,上面有一堆物品,這張桌子就代表著一個層疊上下文。 如果在第一張桌子旁還有第二張桌子,那第二張桌子就代表著另一個層疊上下文。
現在想象在第一張桌子上有四個小方塊,他們都直接放在桌子上。 在這四個小方塊之上有一片玻璃,而在玻璃片上有一盤水果。 這些方塊、玻璃片、水果盤,各自都代表著層疊上下文中一個不同的層疊層,而這個層疊上下文就是桌子。
每一個網頁都有一個預設的層疊上下文。 這個層疊上下文(桌子)的根源就是 <html></html>
。 html標籤中的一切都被置於這個預設的層疊上下文的一個層疊層上(物品放在桌子上)。
當你給一個定位元素賦予了除 auto
外的 z-index 值時,你就建立了一個新的層疊上下文,其中有著獨立於頁面上其他層疊上下文和層疊層的層疊層, 這就相當於你把另一張桌子帶到了房間裡。

層疊上下文1 (Stacking Context 1)是由文件根元素形成的, 層疊上下文2和3 (Stacking Context 2, 3) 都是層疊上下文1 (Stacking Context 1) 上的層疊層。 他們各自也都形成了新的層疊上下文,其中包含著新的層疊層。
在層疊上下文中,其子元素按照上面解釋的規則進行層疊。形成層疊上下文的方法有:
- 根元素
<html></html>
-
position
值為absolute | relative
,且z-index
值不為auto
-
position
值為fixed | sticky
-
z-index
值不為auto
的flex元素,即:父元素display: flex | inline-flex
-
opacity
屬性值小於1
的元素 -
transform
屬性值不為none
的元素 -
mix-blend-mode
屬性值不為normal
的元素 -
filter
、perspective
、clip-path
、mask
、mask-image
、mask-border
、motion-path
值不為none
的元素 -
perspective
值不為none
的元素 -
isolation
屬性被設定為isolate
的元素 -
will-change
中指定了任意 CSS 屬性,即便你沒有直接指定這些屬性的值 -
-webkit-overflow-scrolling
屬性被設定touch
的元素
總結:
- 層疊上下文可以包含在其他層疊上下文中,並且一起組建了一個有層級的層疊上下文
- 每個層疊上下文完全獨立於它的兄弟元素,當處理層疊時只考慮子元素,這裡類似於BFC
- 每個層疊上下文是自包含的:當元素的內容發生層疊後,整個該元素將會 在父級疊上下文 中按順序進行層疊
2. 層疊等級 (Stacking Level)
層疊等級(層疊水平, Stacking Level) 決定了同一個層疊上下文中元素在z軸上的顯示順序的 概念 ;
- 普通元素的層疊等級優先由其所在的層疊上下文決定
- 層疊等級的比較只有在同一個層疊上下文元素中才有意義
- 在同一個層疊上下文中,層疊等級描述定義的是該層疊上下文中的元素在Z軸上的上下順序
注意,層疊等級並不一定由 z-index 決定,只有定位元素的層疊等級才由 z-index 決定,其他型別元素的層疊等級由層疊順序、他們在HTML中出現的順序、他們的父級以上元素的層疊等級一同決定,詳細的規則見下面層疊順序的介紹。
3. z-index
在 CSS 2.1 中, 所有的盒模型元素都處於三維座標系中。 除了我們常用的橫座標和縱座標, 盒模型元素還可以沿著"z 軸"層疊擺放, 當他們相互覆蓋時, z 軸順序就變得十分重要。
-- CSS 2.1 Section 9.9.1 - Layered presentation
z-index 只適用於定位的元素,對非定位元素無效,它可以被設定為正整數、負整數、0、auto,如果一個定位元素沒有設定 z-index,那麼預設為auto;
元素的 z-index 值只在同一個層疊上下文中有意義。如果父級層疊上下文的層疊等級低於另一個層疊上下文的,那麼它 z-index 設的再高也沒用。所以如果你遇到 z-index 值設了很大,但是不起作用的話,就去看看它的父級層疊上下文是否被其他層疊上下文蓋住了。
4. 層疊順序 (Stacking Order)
層疊順序(層疊次序, 堆疊順序, Stacking Order) 描述的是元素在同一個層疊上下文中的順序 規則 ,從層疊的底部開始,共有七種層疊順序:
- 背景和邊框 :形成層疊上下文的元素的背景和邊框。
- 負z-index值 :層疊上下文內有著負z-index值的定位子元素,負的越大層疊等級越低;
- 塊級盒 :文件流中塊級、非定位子元素;
- 浮動盒 :非定位浮動元素;
- 行內盒 :文件流中行內、非定位子元素;
- z-index: 0 :z-index為0或auto的定位元素, 這些元素形成了新的層疊上下文;
- 正z-index值 :z-index 為正的定位元素,正的越大層疊等級越高;
同一個層疊順序的元素按照在HTML裡出現的順序層疊;第7級順序的元素會顯示在之前順序元素的上方,也就是看起來覆蓋了更低階的元素:

5. 實戰
5.1 普通情況
三個 relative
定位的 div
塊中各有 absolute
的不同顏色的 span.red
、 span.green
、 span.blue
,它們都設定了 position: absolute
;
那麼當沒有元素包含z-index屬性時,這個例子中的元素按照如下順序層疊(從底到頂順序):
- 根元素的背景和邊界
- 塊級非定位元素按HTML中的出現順序層疊
- 行內非定位元素按HTML中的出現順序層疊
- 定位元素按HTML中的出現順序層疊
紅綠藍都屬於 z-index 為auto的定位元素,因此按照7層層疊順序規則來說同屬於層疊順序第6級,所以按HTML中的出現順序層疊: 紅->綠->藍
5.2 在相同層疊上下文的父元素內的情況
紅綠位於一個 div.first-box
下,藍位於 div.second-box
下,紅綠藍都設定了 position: absolute
, first-box
與 second-box
都設定了 position: relative
;
這個例子中,紅藍綠元素的父元素 first-box
與 second-box
都沒有生成新的層疊上下文,都屬於根層疊上下文中的元素,且都是層疊順序第6級,所以按HTML中的出現順序層疊: 紅->綠->藍
5.3 給子元素增加 z-index
紅綠位於一個 div.first-box
下,藍黃位於 div.second-box
下,紅綠藍都設定了 position: absolute
,如果這時給綠加一個屬性 z-index: 1
,那麼此時 .green
位於最上面;
如果再在 .second-box
下 .green
後加一個絕對定位的 span.gold
,設定 z-index: -1
,那麼它將位於紅綠藍的下面;
這個例子中,紅藍綠黃元素的父元素中都沒有生成新的層疊上下文,都屬於根層疊上下文中的元素
- 紅藍都沒有設定 z-index,同屬於層疊順序中的第6級,按HTML中的出現順序層疊;
- 綠設定了正的 z-index,屬於第7級;
- 黃設定了負的 z-index,屬於第2級;
所以這個例子中的從底到高顯示的順序就是: 黃->紅->藍->綠
5.4 在不同層疊上下文的父元素內的情況
紅綠位於一個 div.first-box
下,藍位於 div.second-box
下,紅綠藍都設定了 position: absolute
,如果 first-box
的z-index設定的比 second-box
的大,那麼此時無論藍的 z-index 設定的多大 z-index: 999
,藍都位於紅綠的下面;如果我們只更改紅綠的z-index值,由於這兩個元素都在父元素 first-box
產生的層疊上下文中,此時誰的z-index值大,誰在上面;
這個例子中,紅綠藍都屬於設定了z-index的定位元素,不過他們的父元素建立了新的層疊上下文;
- 紅綠的父元素
first-box
是設定了正z-index的定位元素,因此建立了一個層疊上下文,屬於層疊順序中的第7級; - 藍的父元素
second-box
也同樣建立了一個層疊上下文,屬於層疊順序中的第6級; - 按照層疊順序,
first-box
中所有元素都排在second-box
上; - 紅綠都屬於層疊上下文
first-box
中且設定了不同的正 z-index,都屬於層疊順序中第7級; - 藍屬於層疊上下文
second-box
,且設定了一個很大的正 z-index,屬於層疊元素中第7級; - 雖然藍的 z-index 很大,但是因為
second-box
的層疊等級比first-box
小,因此位於紅綠之下;
所以這個例子中從低到到顯示的順序: 藍->紅->綠
(我遇到的的情況就屬於這個例子類似情形)
5.5 給子元素設定 opacity
紅綠位於 div.first-box
下,藍位於 div.second-box
下,紅綠藍都設定了 position: absolute
,綠設定了 z-index: 1
,那麼此時綠位於紅藍的最上面;
如果此時給 first-box
設定 opacity: .99
,這時無論紅綠的 z-index 設定的多大 z-index: 999
,藍都位於紅綠的上面;
如果再在 .second-box
下 .green
後加一個 span.gold
,設定 z-index: -1
,那麼它將位於紅綠藍的下面;
之前已經介紹了,設定 opacity
也可以形成層疊上下文,因此:
-
first-box
設定了opacity
,first-box
成為了一個新的層疊上下文; -
second-box
沒有形成新的層疊上下文,因此其中的元素都屬於根層疊上下文; - 黃屬於層疊順序中第2級,藍屬於層疊順序中第6級,紅綠屬於第7級;
所以這個例子中從低到到顯示的順序: 黃->紅->綠->藍
網上的帖子大多深淺不一,甚至有些前後矛盾,在下的文章都是學習過程中的總結,如果發現錯誤,歡迎留言指出~