第一部分,position: fixed失效的問題;
第二部分,瞭解一下由此扯出的一個Stacking Context層疊上下文。
文中大部分內容來自於我對W3C文檔及一些英文資料的理解,如有不明確或表述不準確的地方還請指出。
關於 position: fixed
position: fixed
在日常佈局中比較常用,如移動端頭部和底部導航定位、模態框、懸浮按鈕等,設置了這個屬性值得元素會相對於屏幕視口(viewport)進行定位,其位置在屏幕進行滾動時會保持不變,不佔用文檔流中的位置,而且打印時這個元素會出現在 每一頁 的相同位置。設置了 position: fixed
的元素最終的位置由它的 top
, right
, bottom
, left
來決定,這個值會創建一個新的 stacking context
(不知道這個詞應該如何準確地翻譯,姑且認爲是層疊上下文吧)。
但是,有些情況下,這種定位方式會失效,使得元素相對於視窗定位的定位不符合預期(其實是 fixed
定位的參考元素變了)。當該元素的父元素中(廣義,包含祖先元素)有元素的 transform
或 perspective
的值不是 none
,該元素就會相對於這個父元素而不是視口進行定位。
具體的原因是這樣:
Specifying a value other than none for the transform property establishes a new local coordinate system at the element that it is applied to. The mapping from where the element would have rendered into that local coordinate system is given by the element’s transformation matrix. Transformations are cumulative. That is, elements establish their local coordinate system within the coordinate system of their parent. From the perspective of the user, an element effectively accumulates all the transform properties of its ancestors as well as any local transform applied to it. The accumulation of these transforms defines a current transformation matrix for the element.
解釋一下,transform
或 perspective
的非 none
值會影響元素的包含塊和層疊上下文,這些值會在應用它的元素上建立一個局部的座標系(X軸向右水平增加; Y軸垂直向下增加),由變換矩陣(transform
的值)給出元素到該局部座標系的映射,而且 transform
帶來的局部座標系的改變是可以累積的——也就是說,子元素會在它的父元素的座標系內建立子元素自己的局部座標系:
父元素的 transform
們一層層積累定義了子元素當前的變換矩陣(一個元素的變換矩陣是從 transform
和 transform-origin
屬性中計算出來的),步驟如下:
- 通過
transform-origin
的值對座標原點X
和Y
的位置進行轉換 - 以變換後的
X
、Y
爲座標原點原點,根據transform
的屬性值進行變換 X
和Y
根據transform-origin
的相反值平移回去
如圖:
transform
會影響最終的渲染效果,但是不影響除overflow外的CSS佈局,當通過 getClientRects()
、getBoundingClientRect()
這些接口獲取 client rectangles
時,transform
的效果也會被考慮進去。
可以應用 transform
的元素(transformable elements
)有:
- 滿足CSS盒模型的塊級元素或行內元素,或者它的 display
值爲 table-row
, table-row-group
, table-header-group
, table-footer-group
, table-cell
, table-caption
中的一個
- SVG
命名空間中具有 transform
, patternTransform
或 gradientTransform
屬性的元素
p.s. 關於上面的應用transform後元素位置的計算方式,是我按照自己的理解翻譯了原文的內容,如有不準確的地方還請指出。原文如下:
>
1. Start with the identity matrix.
2. Translate by the computed X and Y of transform-origin
3. Multiply by each of the transform functions in transform property from left to right
4. Translate by the negated computed X and Y values of transform-origin
參考文檔:
The transform Property - W3C Working Draft - 30 November 2017
The Transform Rendering Model - W3C Working Draft - 30 November 2017
層疊上下文 Stacking Context
通過上文我們知道了有層疊上下文這麼一個東西,層疊上下文是HTML元素的三維概念,這些HTML元素在一條假想的相對於面向(電腦屏幕的)視窗z軸上延伸,HTML元素依據其自身屬性按照優先級順序佔用層疊上下文的空間。和BFC還有IFC這些xx context一樣,創建層疊上下文也是有條件的,文檔中的層疊上下文由滿足以下任意一個條件的元素形成:
- 根元素 (HTML),
- z-index 值不爲
auto
的 絕對/相對定位元素 - position 不是 static 的元素(sticky 也會創建層疊上下文,這是一個神奇的實驗中的屬性值)
- 一個 z-index 值不爲
auto
的 flex 項目 (flex item) - opacity 屬性值小於 1 的元素(參考 the specification for opacity)
transform
屬性值不爲none
的元素mix-blend-mode
屬性值不爲normal
的元素- 有
transform
、filter
、perspective
、clip-path
、mask / mask-image / mask-border
這些屬性中任意一個或多個屬性的元素 isolation
屬性被設置爲isolate
的元素- 在
will-change 中指定了任意
CSS` 屬性的元素(即使沒有直接指定這些屬性的值) -webkit-overflow-scrolling
屬性被設置touch
的元素- 設置了
transform-style: preserve-3d
的元素
在層疊上下文中的子元素也會按照上面的規則進行層疊,子元素的 z-index
值只在父級層疊上下文中有意義,子級層疊上下文被自動視爲父級層疊上下文的一個獨立單元。
但是,並不是創建了新的層疊上下文的元素並不一定都會對其擁有position: fixed的子元素的效果產生影響,
在Chrome(Blink內核)中,可以明確看到產生了影響的是:
transform
屬性值不爲none
的元素- 設置了
transform-style: preserve-3d
的元素 perspective
值不爲none
的元素- 在
will-change
中指定了任意CSS
屬性的元素
但是,在不同的瀏覽器內核下,上述結論也會有所差異,例如,在 Safari(Webkit內核) 中,只有transform
屬性值不爲 none
的元素會對 fixed
定位的效果產生影響.
參考文檔: