深入淺出 CSS 層疊上下文

我們在編寫 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)說明以舒,ParentChild 元素的父子節(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-1box-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-1Child-2 元素分別處于各自的父級層疊上下文元素 Parent-1Parent-2 中。由于 Parent-1 是層疊于 Parent-2 之上的雌澄,即使 Child-1z-index: -1 屬性值小于 Child-2z-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)建層疊上下文的元素县匠;
7 階層疊順序圖

注意事項

  • 層疊順序的第 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)建層疊上下文需要同時滿足兩個條件:

    1. 父元素設(shè)置了display: flex / display: inline-flex
    2. 自身 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 元素之下。

固定定位(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 上泌神,歡迎大家交流良漱。

Reference

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市欢际,隨后出現(xiàn)的幾起案子母市,更是在濱河造成了極大的恐慌,老刑警劉巖损趋,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件患久,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蒋失,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進(jìn)店門返帕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人篙挽,你說我怎么就攤上這事荆萤。” “怎么了铣卡?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵链韭,是天一觀的道長。 經(jīng)常有香客問我煮落,道長敞峭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任蝉仇,我火速辦了婚禮儡陨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘量淌。我一直安慰自己骗村,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布呀枢。 她就那樣靜靜地躺著胚股,像睡著了一般。 火紅的嫁衣襯著肌膚如雪裙秋。 梳的紋絲不亂的頭發(fā)上琅拌,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天,我揣著相機(jī)與錄音摘刑,去河邊找鬼进宝。 笑死,一個胖子當(dāng)著我的面吹牛枷恕,可吹牛的內(nèi)容都是我干的党晋。 我是一名探鬼主播,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼徐块,長吁一口氣:“原來是場噩夢啊……” “哼未玻!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起胡控,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤扳剿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后昼激,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體庇绽,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡锡搜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了瞧掺。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片余爆。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖夸盟,靈堂內(nèi)的尸體忽然破棺而出蛾方,到底是詐尸還是另有隱情,我是刑警寧澤上陕,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布桩砰,位于F島的核電站,受9級特大地震影響释簿,放射性物質(zhì)發(fā)生泄漏亚隅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一庶溶、第九天 我趴在偏房一處隱蔽的房頂上張望煮纵。 院中可真熱鬧,春花似錦偏螺、人聲如沸行疏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽酿联。三九已至,卻和暖如春夺巩,著一層夾襖步出監(jiān)牢的瞬間贞让,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工柳譬, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留喳张,地道東北人。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓美澳,卻偏偏與公主長得像销部,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子人柿,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內(nèi)容