深入理解z-index
要解決的問題
在頁面編寫的過程中,經常需要處理元素的重疊。重疊的順序不當則容易造成元素被錯誤地遮蓋等現象。一般地,有很多人認為只需要指定元素的z-index即可調整重疊的順序,但是實際上並不是這樣的。
重新理解頁面維度
當我們開啟一個網頁,我們會看到一個二維的世界。在這個二維的世界裡,頁面裡的box(盒子)各有自己的x座標和y座標。比如,在下圖所示的頁面結構裡面,有四個div元素。它們分別具有自己的寬和高,而每個box左上角的x和y座標就分別是這個box在頁面中的x和y座標。因而,在我們直觀的感知裡,頁面是二維的。
然而,頁面實際上還有第三個維度。垂直於x軸與y軸的方向,存在一個c軸。c軸的正方向指向使用者。對於一個頁面上的box,它一定有一個c座標。注意,此處的c座標並不是z-index。下圖中的座標名有誤,z應該是c。
兩個重疊的box最能證明這第三個維度的存在,如果頁面上有兩個元素是重疊的,那麼瀏覽器的渲染引擎必須決定哪一個box的重疊部分要被放在前面。決定的方法很簡單,就是直接比較這兩個元素的c座標。c座標大的那一個,就被渲染在前面;反之,則被壓在下面。
你不能把c座標的大小理解成離使用者的遠近,因為如果那樣理解,那麼應該有“近大遠小”現象。然而事實上,c座標大的box並不會變小,只是被瀏覽器渲染在c座標小的box前面而已。就好像在現實生活中,我們把兩張卡片疊在一起,它們會有上下之分,但是看起來兩張卡片的大小並不會有所改變(因為它們足夠薄且小)。會產生近大遠小現象的應該是z座標,學過一點空間幾何的人都應該熟悉。這也是我稱這個維度為c座標而非z座標的原因。
下面我們一起來探究一下頁面box之間是如何重疊的,換句話說,瀏覽器是怎麼決定一個box的c座標的。
預設重疊
對於重疊的元素,瀏覽器預設會按下面的順序重疊。編號越大的box型別,所擁有的c座標就越大,因此排在前面。
正常流當中的block level的box
浮動的元素
正常流當中inline level或者inline-block level的box
position值不是static(非正常流中)的box
這裡並不是完整的列表,因為我略去了一些後面才會提到的內容。下面是一個示例:
可以看到,上述四種不同型別的box中,具有最大c座標的是第4種,它能夠覆蓋其他所有三種元素。
你需要注意的一點是,不要用DOM樹的結構來理解重疊。DOM樹中下一級的box完全可以重疊在上一級box之上。這都和Stacking Context有關,我們接下來就詳細解釋一下。
Stacking-Context
上述四種box型別的重疊規律,當且僅當這些box在同一個Stacking Context的時候生效。不同Stacking Context中的box之間的重疊在下面會提到。
什麼是Stacking Context?假設你正在玩貼紙,將很多貼紙貼到同一張紙上,並且貼紙之間可能產生重疊。Stacking Context就像是這張紙。瀏覽器首先按照預設的重疊規律,將同一個Stacking Context下的所有元素排好順序,然後按照這個順序渲染到Stacking Context上。例如,在下面的DOM結構中,有5個box,其中node1是一個Stacking Context,其他的都不是。
在渲染的時候,瀏覽器將node1當做畫板,其他box都是貼紙。在決定貼紙順序的時候,瀏覽器是不會考慮它們DOM結構上的父子關係的。也就是說,node3-1完全可以被渲染在node2-1的後面,只要在前面所說的預設重疊順序中,node3-1具有的c座標比node2-1來得低即可。
整個“貼紙”的過程如下圖。可以看到,瀏覽器在當前Stacking Context中,無視了“貼紙”們的DOM結構之間的關係,而是通過我們前面提到的預設重疊順序決定“貼紙”的先後關係。決定之後,瀏覽器將所有“貼紙”貼到Stacking Context上,這個過程稱作Composite(組合)。
什麼樣的元素是一個Stacking-Context
符合下面要求的頁面box,都是一個Stacking Context。
根元素(html元素)
position是absolute或者relative,並且z-index不是auto的元素
是flex item,並且z-index不是auto的元素
opacity值小於1的元素
在mobile webkit以及Chrome 22+中,position: fixed的元素
多個Stacking-Context之間的重疊
由上面產生Stacking Context的條件,我們可以知道,一個頁面上一般有多個Stacking Context。那麼多個Stacking Context之間如何決定重疊順序呢?瀏覽器一般按下面的規則來決定其順序,這實際上就是之前我們提到的預設重疊順序的完整版。
Stacking Context的背景和邊框
具有負的z-index的,且position值不是static(非正常流中)的子box的Stacking Context,且z-index數值越小,其c座標越小
正常流當中的block level的box
浮動的元素
正常流當中inline level或者inline-block level的box
position值不是static(非正常流中)的box,且z-index為0或者auto
具有正的z-index的,且position值不是static(非正常流中)的子box的Stacking Context,且z-index數值越小,其c座標越小
你需要注意到的是,z-index只能作用在position值不是static的box上方能起效。下面的例子可以說明多個Stacking Context之間的重疊規律。
在這裡,.wrapper、#b1和#b2分別都是一個Stacking Context。#b1和#b2是.wrapper的子Stacking Context,瀏覽器會首先組合#b1以及組合#b2,之後再將#b1和#b2組合到.wrapper上。由於#b1具有正的z-index,而#b2具有負的z-index,所以#b1被組合到了#b2的上面。
你可以試著把#b1的z-index改成-2,那麼它就變成了第二類的box(和#b2一樣),又因為它的z-index比#b2來得小,所以它會被組合到#b2之後。
總結
z-index只在同一個Stacking Context的組合過程中,參與各個子box的重疊順序的決定。但是頁面box的重疊關係並非僅僅和z-index有關。清楚地認識z-index的工作原理,有助於你寫出更有效率的樣式表。
原文釋出時間為:2018-10-11
原文作者:桃翁