一般咱們在學習/瞭解CSS的時候,並不會直接接觸/瞭解到stacking context的規則,甚至在初學的時,僅僅接觸到z-index、知道能夠經過z-index控制元素顯示的先後順序,卻經常由此碰到各式各樣匪夷所思的bug,這兩天我也趟了一次z-index渾水,搞明白了z-index扯出的這一系列stacking context坑。css
在W3C document裏對z-index的解釋是:html
The z-index attribute lets you adjust the order of the layering of objects when rendering content.
It means that CSS style rules allow you to position boxes on layers in addition to the normal rendering layer (layer 0). The Z position of each layer is expressed as an integer representing the stacking order for rendering. Greater numbers mean closer to the observer.css3
簡言之咱們經過z-index在layer 0上控制「已定位」元素的前後順序,其值越大元素越靠近用戶。#這裏layer 0指代root節點即html元素web
顯然僅僅知道z-index的做用並不能解釋現實中諸多怪異的CSS z-index定位表現,如同上一節中提到指代root節點的layer 0,在CSS中也可構造出與其類似的結構,這種提供z-index棧空間特性並影響子元素渲染順序的結構,咱們稱之爲stacking context。chrome
知足下面規則的元素將會構造出一個 Stacking Context 結構:express
*注意除了前兩條以外有如此多知足建立stacking context的條件,這也是形成諸多bug的源泉,好比opacity<1
瀏覽器
對於一個stacking context內部的元素,若是這個元素沒有造成stacking context,其z-index值是auto(但其實若是這個元素沒有造成stacking context,z-index屬性對這個元素的表現根本沒有意義,咱們能夠理解爲這個元素和其parent stacking context是一體的)。less
咱們經過給已定位元素(position: absolute or relative)指定z-index值以改變元素在其parent stacking context中Z軸的「相對偏移」量。這裏的「相對偏移」指的是以parent stacking context爲基準,相對於其它兄弟元素距離用戶遠近的順序。async
因爲造成stacking context的元素其z-index屬性並不對內部元素產生影響,所以其子元素以其(parent stacking context)爲z-index相對基準點即z-index: auto,這些子元素的stacking context兄弟元素按照下面的遠近順序展現在屏幕:ide
遠 ---------> 近
parent stacking context >> z-index < 0 >> 非stacking context元素(z-index: auto) >> z-index >= 0
*注意在stacking context中的元素不會遠於parent stacking context
先上一段極其容易產生困惑的代碼(不支持embedded scripts略卵疼):
See the Pen yNJRKX by abruzzi (@abruzzi) on CodePen.
<script async src="//assets.codepen.io/assets/embed/ei.js"></script>
http://codepen.io/abruzzi/pen/yNJRKX
如上文說起,這裏box3和box5經過opacity: .99建立了一個stacking context,但box3卻覆蓋在了box4之上,翻查W3C文檔並無這樣一條規則定義stacking context元素默認在非stacking context元素之上;咱們修改了box5的z-index屬性爲負值,box5依然在box6之上;咱們又經過position: relative & z-index: -1建立了box7的stacking context,其表現倒是正常的(話說回來,z-index: auto自己也沒法造成stacking context,所以根本沒有存在相似「stacking context元素默認在非stacking context元素之上」定義的意義)
在W3C文檔的某個角落,咱們能夠究其原委:
If an element with opacity less than 1 is not positioned, implementations must paint the layer it creates, within its parent stacking context, at the same stacking order that would be used if it were a positioned element with ‘z-index: 0’ and ‘opacity: 1’.
簡言之,若是一個元素不是經過「定位」(position: absolute or relative)實現了stacking context,它將會以z-index: 0(高於auto)被看待,所以不管如何更改非「定位」元素的z-index都是無效的。
雖然文檔中只提到opacity less 1構成的stacking context被看作z-index: 0,但經過測試,能夠發現其餘非「定位」方式建立的stacking context擁有與opacity less 1一致的表現。
以上即是我對CSS stacking context中形成諸多匪夷所思現象緣由的總結。
*注:下文內容與題目無關,僅爲做者思路延展
瀏覽器在解析渲染document時,內部的處理並未直接暴露給咱們,其中很重要的結構就是Layers。在這些未暴露的步驟中,RenderLayers負責渲染整個DOM子樹結構,GraphicLayers則負責渲染RenderLayers中的子樹,GraphicLayer把所負責的子樹做爲textures(能夠理解爲內存間移動的位圖)upload到GPU,以實現高速繪製,也於是能夠大幅優化這些屬性帶來的動畫效果。
包括opacity在內,這些可產生stacking context的屬性和建立特殊GraphicLayers的屬性(opacity, filter, transfrom3d等)都很類似,但某些屬性卻又沒法吻合(例如2d transfrom),冥冥之中總以爲二者原理上有類似之處,能力有限,只好留一坑。
在定位中有一個跟stacking content無關但又較爲相近的危險Bug,注意下面代碼中.inner的定位:
See the Pen VLKeZP by abruzzi (@abruzzi) on CodePen.
<script async src="//assets.codepen.io/assets/embed/ei.js"></script>
http://codepen.io/abruzzi/pen/VLKeZP
對於聲明transfrom值非none元素,其子元素中若存在position: fixed將以聲明transform的最近祖先做爲基準而定位,這是由於transfrom值非none的元素定義了一個局部座標系統,致使postion: fixed以此座標系統計算佈局。
目前這個bug仍未被解決,官方建議避免在transform元素下作fixed定位。