前端基礎
-深入解析 CSS z-index 和 stacking context 堆疊上下文
你有發生過設定 z-index: 999 的元素蓋不過 z-index: 0 的元素嗎,今天我通過這篇文,帶你清楚了解 z-index 以及瀏覽器的元素先後比較機制。
z-index 是什麼
z-index
在 CSS 中很重要的觀念,z-index 是用來控制元素在 Z 軸上的堆疊順序(即前後順序)的屬性。當多個元素重疊時,z-index 會決定哪些元素會顯示在前面,哪些會被擋在後面。
可以想像他是垂直穿過電腦螢幕,射向你的一條線,也就是 z-index 越高的元素就越靠近你,他就會蓋在其他元素上面。
怎麼使用 z-index
z-index 只能用在定位元素上,也就是設定 position: relative、absolute、fiexd、sticky
的元素上面。
/* 非 position: static 的元素才能設定 z-index */
div {
position: relative;
position: absolute;
position: fixed;
position: sticky;
}
瀏覽器的預設圖層順序
在沒有設置 z-index
的情況下,瀏覽器會按照元素在 HTML 中的出現順序進行堆疊,後寫的元素會在先寫的元素上面:
<div class="div1"></div>
<div class="div2"></div>
div {
width: 150px;
height: 150px;
border-radius: 16px;
}
.div1 {
background: var(--blue);
}
.div2 {
transform: translate(25%, -25%);
background: var(--red);
}
在這個例子中,由於 .div2
後寫,所以它會顯示在 .div1
之上。
當設定 z-index 後,就會變得較複雜,讓我們接著看下去。
一、兄弟元素的 z-index 比較:
當兩個元素為兄弟元素時,堆疊順序的比較可以分為以下幾種情況:
- 相同條件
- 不同條件
A、相同條件
- 都不是定位元素
- 都是定位元素但都沒有設定 z-index
- 都是定位元素且有相同的 z-index
這些情況下,後寫的元素會蓋在先寫的元素上面。
<div class="div1"></div>
<div class="div2"></div>
div {
position: relative;
width: 150px;
height: 150px;
border-radius: 16px;
}
.div1 {
background: var(--blue);
}
.div2 {
transform: translate(25%, -25%);
background: var(--red);
}
上面都是定位元素,但因為都沒有設定 z-index,所以會依照瀏覽器預設的排列。
B、不同條件
- 當兄弟元素都有設定
z-index
,z-index
較高的元素會蓋在上面。
div {
position: relative;
width: 150px;
height: 150px;
border-radius: 16px;
}
.div1 {
background: var(--blue);
z-index: 2;
}
.div2 {
transform: translate(25%, -25%);
background: var(--red);
z-index: 1;
}
此時 div1 因為 z-index 較高的原因,會在 div2 上面。
而當一個元素有設定 z-index,另一個沒有時,會發生甚麼事情呢?可以分為三種情況來看
- 設定 z-index = 0,則依照瀏覽器的預設情況比較,後寫的在先寫的上面。
- 設定 z-index > 0,則會蓋住沒有設 z-index 的元素。
- 設定 z-index < 0,則會被沒有設 z-index 的元素蓋住。
設定 z-index = 0
此時 div1 會在 div2 下面,因為 div2 是後寫的。
.div1 {
background: var(--blue);
position: absolute;
z-index: 0;
}
.div2 {
background: var(--red);
position: absolute;
/* 沒有設定 z-index */
}
設定 z-index > 0
此時 div1 會在 div2 上面,因為 div1 的 z-index > 0。
.div1 {
background: var(--blue);
position: absolute;
z-index: 1;
}
.div2 {
background: var(--red);
position: absolute;
/* 沒有設定 z-index */
}
設定 z-index < 0
此時 div2 會在 div1 上面,因為 div1 的 z-index < 0。
.div1 {
background: var(--blue);
position: absolute;
z-index: -1;
}
.div2 {
background: var(--red);
position: absolute;
/* 沒有設定 z-index */
}
二、子元素與父元素的 z-index 比較:
絕大多數情況下,子元素的層級都大於父元素。但有一個例外:
- 如果子元素有 position 屬性且 z-index < 0,它會被沒有設定 z-index 的父元素蓋住。
<div class="parent">
<div class="child"></div>
</div>
/* 有 position 屬性但 z-index < 0 的會被沒有 position 的蓋住 */
/* 這個例子中,因為 div2 的 z-index 為負,會被沒有設定 z-index 的父元素蓋住 */
.parent {
position: unset;
background: var(--blue);
}
.child {
position: relative;
z-index: -1;
background: var(--red);
transform: translate(25%, -25%);
}
要注意的是,只要父元素一設定 z-index,不管大小,都會被子元素蓋過。
.parent {
position: relative;
z-index: 100; /* 儘管父元素的 z-index 比子元素大,還是會被子元素蓋過 */
background: var(--blue);
}
.child {
position: relative;
z-index: -1;
transform: translate(75%, 75%);
background: var(--red);
}
三、子元素和其它親戚元素的 z-index 比較:
親戚元素是指和父母的兄弟姊妹(阿姨叔叔)或表哥表弟元素比較。如下面的 code,you
和 uncle
或 cousin
比較。
<div class="uncle">
<div class="cousin"></div>
</div>
<div class="father">
<div class="you"></div>
</div>
當和叔叔元素、表弟元素比較時,都必須考慮父元素的 position 屬性和 z-index 值:
若 father
和 uncle
都為定位元素(有 position 屬性),且 z-index:father
> uncle
,則 cousin
永遠不可能比 you
層級高。
/* father > uncle 所以 you 永遠 > cousin */
.uncle {
position: relative;
z-index: 1;
background: var(--blue);
}
.cousin {
position: relative;
z-index: 100;
transform: translate(25%, 25%);
background: var(--red);
}
.father {
position: relative;
z-index: 2;
}
.you {
position: relative;
z-index: -1;
transform: translate(50%, -25%);
background: var(--yellow);
}
而若父元素沒有設定 position,則子元素會與父元素的兄弟元素(叔叔元素)進行比較,忽略父元素。
而由於 you 寫在 uncle 之後,且都沒有設定 z-index,這裡就會照瀏覽器預設的方式排列。you 會在 uncle 上面。
<div class="uncle"></div>
<div class="father">
<div class="you"></div>
</div>
/* 若父元素 div2 沒有設定 position,則子元素 div3 會忽略父元素跟其他父元素的兄弟元素 div1 比較 */
/* div3 寫在 div1 後,所以 div3 > div1 */
.uncle {
position: relative;
background: var(--blue);
}
.father {
position: unset;
}
.you {
position: relative;
transform: translate(75%, -25%);
background: var(--green);
}
為什麼 z-index 這麼複雜?
其實,z-index
的複雜性來自於堆疊上下文(Stacking Context)。理解這一點後,z-index
的行為就不再那麼神秘了。
堆疊上下文(Stacking Context)
當一個定位元素有被設定 z-index 時,他就會創建堆疊上下文。而堆疊上下文具有三種特性:
- 獨立性:每個堆疊上下文內的元素和外部的元素是獨立的。這意味著內部元素的 z-index 和外部元素的 z-index 沒有關係。
- 上升性:每個堆疊上下文的最外層元素會上升到根堆疊上下文(root),並與其他堆疊上下文的元素進行比較。
- 內部排序性:每個堆疊上下文內部元素的堆疊順序如下(從低到高):
- 背景和邊框
- 負
z-index
的元素 z-index
為auto
或0
的元素- 正
z-index
的元素
當知道這三個特性後,再回去看實例,應該就能理解 z-index 的比較了。
總結
z-index
的比較相對複雜,但大多數場景下只需記住一個簡單的規則:”子元素的堆疊順序通常不能超越父元素的堆疊上下文” 就能解決大部分的應用場景了~