CSS Stacking Context 裏那些不爲人知的坑

一般咱們在學習/瞭解CSS的時候,並不會直接接觸/瞭解到stacking context的規則,甚至在初學的時,僅僅接觸到z-index、知道能夠經過z-index控制元素顯示的先後順序,卻經常由此碰到各式各樣匪夷所思的bug,這兩天我也趟了一次z-index渾水,搞明白了z-index扯出的這一系列stacking context坑。css

什麼是z-index

在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

什麼是Stacking Context

顯然僅僅知道z-index的做用並不能解釋現實中諸多怪異的CSS z-index定位表現,如同上一節中提到指代root節點的layer 0,在CSS中也可構造出與其類似的結構,這種提供z-index棧空間特性並影響子元素渲染順序的結構,咱們稱之爲stacking context。chrome

怎樣的CSS屬性可使element造成一個Stacking Context

知足下面規則的元素將會構造出一個 Stacking Context 結構:express

  • root元素(html)
  • 「已定位」元素(position: absolute or relative)且 指定z-index值非auto的元素
  • flex item且指定z-index值非auto的元素
  • opacity小於1的元素
  • 指定transform值非none的元素
  • 指定mix-blend-mode值非normal的元素
  • 指定filter值非none的元素
  • 指定isolation值爲isolate的元素
  • ==特例 mobile webkit & chrome 22+, 指定position: fixed的元素==
  • 在will-change屬性上指定值爲上述列表任意屬性的元素
  • 指定-webkit-overflow-scrolling值爲touch的元素

*注意除了前兩條以外有如此多知足建立stacking context的條件,這也是形成諸多bug的源泉,好比opacity<1

瀏覽器

Stacking Context有什麼特性

  1. stacking context能夠嵌套
  2. 每一個stacking context相對於兄弟元素是徹底獨立的,其內部規則不會影響到外部
  3. 每一個stacking context元素都會被父stacking context當作一個元素施加stacking規則

對於一個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

非「定位」元素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中形成諸多匪夷所思現象緣由的總結。


*注:下文內容與題目無關,僅爲做者思路延展

什麼是GraphicLayer

瀏覽器在解析渲染document時,內部的處理並未直接暴露給咱們,其中很重要的結構就是Layers。在這些未暴露的步驟中,RenderLayers負責渲染整個DOM子樹結構,GraphicLayers則負責渲染RenderLayers中的子樹,GraphicLayer把所負責的子樹做爲textures(能夠理解爲內存間移動的位圖)upload到GPU,以實現高速繪製,也於是能夠大幅優化這些屬性帶來的動畫效果。

包括opacity在內,這些可產生stacking context的屬性和建立特殊GraphicLayers的屬性(opacity, filter, transfrom3d等)都很類似,但某些屬性卻又沒法吻合(例如2d transfrom),冥冥之中總以爲二者原理上有類似之處,能力有限,只好留一坑。

Fixed定位脫離Viewport的bug

在定位中有一個跟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定位。


by Abruzzi's blog

相關文章
相關標籤/搜索