Sticky
也不是新知識點了,寫這篇文章的緣由是因爲最近在實現效果的過程當中,發現我對 Sticky
的理解有誤差,代碼執行結果不如預期。決定寫篇文章從新學習一次。css
Sticky
(MDN 翻譯成粘性效果)是 CSS
屬性 position
中的一個可選值。跟咱們用得比較多的 static
, fixed
,relative
,absolute
同樣,用來描述元素的定位方式。html
從效果上看,Sticky
像是混合體,頁面滑動到「臨界點」以前表現爲 relative
, 到達「臨界點」時表現爲 fixed
。react
使用 CSS Sticky
只須要兩個條件。git
position: sticky;
top: 0; // right/bottom/left 任一有效值,甚至能夠爲負像素值
複製代碼
top:0
意思是當元素滑動到距離視口 0px 時再繼續滑動,元素吸頂。能夠在 這裏 看效果(試試看修改 top 值)github
沒有 CSS Sticky
以前,相似的效果都是使用 JS 實現。大體步驟以下:npm
fixed
。window.addEventListener('scroll', () => { const rect = elem.getBoundingClientRect(); // 計算目標元素和視口的距離 }) 複製代碼
在 npm 上搜 sticky 關鍵字,也有不少優秀的包可使用。以 react-sticky 爲例,知足條件時會建立 placeholder
元素(防止頁面抖動),同時讓 header
定位爲 fixed
。app
右邊是 Chrome Dev-Tools
的 layers
面板,藍色部分爲生成的 placeholder
。post
兩種方案的火焰圖對比(爲了放大效果,我把 cpu
調慢了 6 倍)學習
使用 CSS Sticky
,工做都交給 GPU
了,不佔用 JS
主線程的資源,在移動端上異常流暢。spa
因爲須要在 scroll event
回調中不斷調用 getBoundingClientRect
,而 getBoundingClientRect
又會觸發頁面重排重繪,稍不留神就掉幀卡頓。僅僅爲了實現這個效果(頁面上沒有其餘內容)大動干戈性價比很低。
結論是:實現 Sticky
效果,優先選擇 CSS Sticky
修改例子,用一個 div 把 Sticky Header
包裹起來,發現 Sticky
效果失效了!!!
...
<div class="wrapper"> <header>Sticky Header</header> </div> ... 複製代碼
根據文檔,Sticky
效果只在 Containing Block 內有效,Containing Block
滑出屏幕時,Stickey Element
也跟着滑走。
修改 wrapper
的高度,看效果。
.wrapper { height: 100px; background-color: #e6e6e6; } 複製代碼
多個 Sticky Element
放在一塊就有了前一個被後一個頂出去的特效,實際上並非真的被頂出去,而是 Containing Block
把它拖走。
修改例子中的代碼,給 #root
加上 overflow: auto
#root { overflow: auto; } 複製代碼
Sticky
效果再次丟失(overflow
設置爲其餘非 visible
的有效值也是一樣效果。)
看了不少相關的文檔,個人出來的結論是:
Sticky Element
的 offset 值是依據 nearest scrolling ancestor
(距離最近的滾動祖先) 計算的,若是沒有匹配上的祖先元素,則使用視口做爲參照物。
問題就出在 overflow-x
或者 overflow-y
其中任一爲非 visible
則認爲是要找的目標元素,而在滾動窗口的過程當中,Sticky Element
和 它找到的目標祖先元素的 offset
值一直沒有改變,因此 Sticky
不起做用。
對症下藥,讓滾動發生在被「誤匹配」上的祖先元素內便可恢復 Sticky Effect
。
#root { overflow: auto; height: 100vh; } 複製代碼
算上 prefixed
,當前 css sticky
手機端兼容性達到 94.14%,若是你所作的業務須要照顧剩下 5.86% 的用戶,也可使用 polyfill 或者 position: fixed
。