我們在編寫 CSS 樣式時不少遇到這樣的疑惑:“為什么這個元素會被覆蓋?”啃炸、“為什么設(shè)置較高的 z-index
值還是不起效果铆隘?”
不妨先想想這個問題,當(dāng)多個 HTML 元素在瀏覽器視窗中發(fā)生重疊時南用,瀏覽器會怎么安排哪個元素顯示在上膀钠、哪個顯示在下呢?其實所有元素在發(fā)生層疊時的表現(xiàn)都是按照一定的優(yōu)先級順序的裹虫,這些順序規(guī)則都是建立在 層疊上下文(The Stacking Context) 這個三維概念中肿嘲,我們一起來了解下。
層疊上下文特性
-
在同一個層疊上下文中筑公,子元素按照 層疊順序 規(guī)則進(jìn)行層疊雳窟;
<style> .wrapper { position: relative; z-index: 1; } .wrapper div:nth-of-type(1) { position: relative; z-index: -1; } .wrapper div:nth-of-type(2) { display: block; } .wrapper div:nth-of-type(3) { float: left; } .wrapper div:nth-of-type(4) { display: inline-block; } .wrapper div:nth-of-type(5) { position: relative; } .wrapper div:nth-of-type(6) { position: absolute; z-index: 1; } </style> <body> <div class="wrapper"> <div>position: relative;<br>z-index: -1;</div> <div>display: block;</div> <div>float: left;</div> <div>display: inline-block;</div> <div>position: relative;</div> <div>position: absolute;<br>z-index: 1;</div> </div> </body>
關(guān)于 層疊順序 的詳解請往下看,完整代碼已上傳 CodePen:https://codepen.io/JunreyCen/pen/WqoLmr
另外匣屡,當(dāng)不同層疊順序的元素相比較時封救,不關(guān)心元素在 DOM 樹中的結(jié)構(gòu)關(guān)系:
<style> .child { position: relative; z-index: -1; } </style> <div class="parent"> <div class="child"></div> </div> <div class="parent" style="opacity: 0.6;"> <div class="child"></div> </div>
左邊實例中
Parent
元素不是層疊上下文元素,所以和Child
元素是處于同一個層疊上下文中捣作,而根據(jù)層疊順序誉结,普通流塊級元素 是疊在 負(fù)z-index
定位元素 之上的。右邊實例中Parent
元素指定屬性opacity: 0.6
券躁,創(chuàng)建了一個層疊上下文惩坑,從而使Child
元素包含在內(nèi),根據(jù)層疊順序 負(fù)z-index
定位元素 會疊在 根元素背景和邊框 之上也拜。這樣的表現(xiàn)說明以舒,Parent
和Child
元素的父子節(jié)點關(guān)系不會影響層疊關(guān)系。 -
在同一個層疊上下文中慢哈,當(dāng)元素的層疊順序相同時蔓钟,按照 元素在 HTML 中出現(xiàn)的順序 進(jìn)行層疊;
<style> .box-1 { position: relative; } .box-2 { position: absolute; top: 50px; left: 50px; } </style> <div class="wrapper"> <div class="box-1"></div> <div class="box-2"></div> </div> <div class="wrapper"> <div class="box-2"></div> <div class="box-1"></div> </div>
所有
z-index
屬性值為0
/auto
的定位元素的層疊順序都是相同的岸军,如實例中的box-1
和box-2
元素奋刽,可以看出它們發(fā)生層疊時遵循的是 在 HTML 中出現(xiàn)的先后順序。 層疊上下文支持嵌套艰赞,子級層疊上下文對于父級層疊上下文是一個獨立單元佣谐,對于兄弟元素來說也是相互獨立的;
-
每個層疊上下文都是自包含的方妖,無論在子級層疊上下文內(nèi)如何 “翻云覆雨”狭魂,也是基于父級層疊上下文的層疊順序下;
<style> .parent-1, .parent-2, .child-1, .child-2 { position: relative; } .parent-1 { z-index: 2; } .parent-2 { z-index: 1; } .child-1 { z-index: -1; } .child-2 { z-index: 10; } </style> <div class="parent-1"> <div class="child-1"></div> </div> <div class="parent-2"> <div class="child-2"></div> </div>
Child-1
和Child-2
元素分別處于各自的父級層疊上下文元素Parent-1
和Parent-2
中。由于Parent-1
是層疊于Parent-2
之上的雌澄,即使Child-1
的z-index: -1
屬性值小于Child-2
的z-index: 10
斋泄,Child-2
也不能逾越其父級上下文覆蓋在Child-1
上面的。換句話說镐牺,子級層疊上下文的層級討論只在其父級層疊上下文中有意義炫掐。
創(chuàng)建層疊上下文
滿足以下 12 個條件中任意一個的元素會創(chuàng)建一個層疊上下文(摘自 MDN ):
1、根元素 <html>
睬涧;
2募胃、z-index
值不為 auto
的相對定位(position: relative
)和絕對定位(position: absolute
);
3畦浓、固定定位(position:fixed
)痹束;
4、z-index
值不為 auto
的伸縮盒模型中的 flex 項目讶请;
5祷嘶、opacity
屬性值小于 1
;
6夺溢、transform
屬性值不為 none
论巍;
7、filter
屬性值不為 none
企垦;
8环壤、mix-blend-mode 屬性值不為 normal
晒来;
9钞诡、perspective 屬性值不為 none
;
10湃崩、isolation 屬性值為 isolate
荧降;
11、will-change 屬性 指定了上述任意一個 CSS 屬性(即便沒有直接指定這些屬性的值)攒读;
12朵诫、-webkit-overflow-scrolling 屬性值為 touch
;
關(guān)于第 11 點中的
will-change
屬性:該屬性主要用于告知瀏覽器元素會發(fā)生哪些變化薄扁,有利于瀏覽器在元素真正變化之前提前做好優(yōu)化準(zhǔn)備剪返。所以當(dāng)will-change
指定了能創(chuàng)建層疊上下文的 CSS 屬性(包括z-index
屬性)時,瀏覽器也會為該元素創(chuàng)建層疊上下文邓梅。
層疊順序
層疊順序就是元素在層疊上下文中的顯示規(guī)則脱盲。在此之前需要提前認(rèn)識一個重要概念:z-index
只會對定位元素(非普通流)有效 !
當(dāng)元素發(fā)生層疊時日缨,會按照下面的 7 階層疊順序來決定元素顯示的前后順序:
1钱反、根元素的背景與邊框;
2、z-index < 0
的定位元素面哥;
3哎壳、普通流中的塊元素(display: block
);
4尚卫、浮動塊元素归榕;
5、普通流中的行內(nèi)元素(display: inline
/ display: inline-block
)吱涉;
6蹲坷、z-index
屬性值為 0
/ auto
的定位元素、其他子級層疊上下文元素邑飒;
7循签、z-index > 0
的定位元素;
- 普通流:不指定
position
或者position: static
疙咸;- 上面第 6 點中的 其他 子級層疊上下文元素:不依賴
z-index
屬性就能創(chuàng)建層疊上下文的元素县匠;
注意事項
-
層疊順序的第 6 階中,
z-index
屬性值為0
/auto
的定位元素 和 不依賴z-index
的子級層疊上下文元素 順序相同撒轮,會遵循 在 HTML 中出現(xiàn)的先后順序 來層疊乞旦;<div class="wrapper"> <div class="box-1" style="opacity: 0.8;"></div> <div class="box-2" style="position: absolute;"></div> </div> <div class="wrapper"> <div class="box-2" style="position: absolute;"></div> <div class="box-1" style="opacity: 0.8;"></div> </div>
-
伸縮盒沒有創(chuàng)建層疊上下文,伸縮盒中
z-index
值不為auto
的 flex 項目才會創(chuàng)建層疊上下文题山。換句話說兰粉,使用這種方式創(chuàng)建層疊上下文需要同時滿足兩個條件:- 父元素設(shè)置了
display: flex
/display: inline-flex
; - 自身
z-index
屬性值不為auto
;
<style> .child { position: relative; z-index: -1; } </style> <div class="container"> <div class="item"> <div class="child"></div> </div> </div> <div class="container" style="display: inline-flex;"> <div class="item" style="z-index: 1;"> <div class="child"></div> </div> </div> <div class="container" style="display: inline-flex;"> <div class="item" style="z-index: -1;"> <div class="child"></div> </div> </div>
左邊實例顶瞳,
Container
玖姑、Item
元素都沒有創(chuàng)建層疊上下文,這兩者與Child
元素都處于同一個層疊上下文中慨菱,按照層疊順序渲染焰络;中間實例,
Container
元素設(shè)置display: inline-flex
成為 Flex 容器符喝,Item
元素設(shè)置z-index: 1
闪彼,則Item
元素創(chuàng)建了層疊上下文,所以即使Child
元素的z-index
值為負(fù)數(shù)协饲,卻依然層疊在Item
元素之上畏腕;右邊實例,把
Item
元素的z-index
值改為-1
茉稠,由于Container
元素作為 Flex 容器是沒有創(chuàng)建層疊上下文的描馅,所以Item
元素會層疊在Container
元素之下。 - 父元素設(shè)置了
固定定位(position: fixed
)的特殊性
我們發(fā)現(xiàn)战惊,單純是絕對/相對定位元素是無法創(chuàng)建一個層疊上下文的流昏,需要同時滿足 z-index
值不為 auto
的條件扎即。然而,固定定位元素就不需要滿足這個條件况凉,顯得尤為特殊谚鄙。
-
固定定位元素?zé)o需滿足
z-index
值不為auto
的條件就可以創(chuàng)建層疊上下文;同樣地刁绒,設(shè)置z-index: auto
并不能撤銷固定定位元素所創(chuàng)建的層疊上下文闷营。<style> .fixed { position: fixed; z-index: auto; } .child { position: relative; z-index: -1; } </style> <div class="fixed"> <div class="child"></div> </div>
-
正常情況下,固定定位是相對于瀏覽器視窗(
viewport
)進(jìn)行定位的知市,但是當(dāng)其祖先元素中存在符合以下任意一個條件的元素時傻盟,固定定位元素會相對于該元素進(jìn)行定位:1、
transform
屬性值不為 none嫂丙;
2娘赴、transform-style: preserve-3d
;
3跟啤、perspective
屬性值不為none
诽表;
4、will-change
屬性 指定了上面 3 個 CSS 屬性中的任意一個隅肥;然而竿奏,這種表現(xiàn)在不同的瀏覽器中有差異,譬如在 Safari 中只有上述第 1 點會對固定定位元素產(chǎn)生影響腥放。
建議
通過對層疊上下文和層疊順序的了解泛啸,我們知道,要控制元素間的層疊關(guān)系除了使用 z-index
屬性外還有很多途徑秃症,而且比使用 z-index
要優(yōu)雅得多候址。濫用 z-index
往往只會把層疊關(guān)系復(fù)雜化,造成代碼難以維護(hù)伍纫。
友情鏈接
學(xué)習(xí)完 CSS 層疊上下文宗雇,如果對 BFC(塊格式化上下文)昂芜、IFC(行內(nèi)格式化上下文)等相關(guān)概念有興趣的莹规,可以學(xué)習(xí)這篇文章:視覺格式化模型 | MDN。
文中涉及到的完整實例代碼已放在 Github 上泌神,歡迎大家交流良漱。