天天看點

CSS Sticky 其實很簡單

為什麼要寫這篇文章

​Sticky​

​​ 也不是新知識點了,寫這篇文章的原因是由于最近在實作效果的過程中,發現我對 ​

​Sticky​

​ 的了解有偏差,代碼執行結果不如預期。決定寫篇文章重新學習一次。

什麼是 Sticky

​Sticky​

​​ (MDN 翻譯成粘性效果)是 ​

​CSS​

​​ 屬性 ​

​position​

​​ 中的一個可選值。跟我們用得比較多的 ​

​static​

​​, ​

​fixed​

​​,​

​relative​

​​,​

​absolute​

​ 一樣,用來描述元素的定位方式。

從效果上看,​

​Sticky​

​​ 像是混合體,頁面滑動到“臨界點”之前表現為 ​

​relative​

​​, 到達“臨界點”時表現為 ​

​fixed​

​。

如何使用

使用 ​

​CSS Sticky​

​ 隻需要兩個條件。

position: sticky;
top: 0; // right/bottom/left 任一有效值,甚至可以為負像素值
複制代碼      

​top:0​

​​ 意思是當元素滑動到距離視口 0px 時再繼續滑動,元素吸頂。可以在 ​​這裡​​ 看效果(試試看修改 top 值)

對比 JS 的實作方案

沒有 ​

​CSS Sticky​

​ 之前,類似的效果都是使用 JS 實作。大緻步驟如下:

  1. 監聽滾動事件,計算目标元素距離視口的距離。
  2. 距離不滿足條件時,按兵不動。
  3. 距離滿足條件時,建立占位元素,修改目标元素定位方式為​

    ​fixed​

    ​。
window.addEventListener('scroll', () => {
    const rect = elem.getBoundingClientRect();
    // 計算目标元素和視口的距離
})
複制代碼      

在 npm 上搜 sticky 關鍵字,也有很多優秀的包可以使用。以 ​​react-sticky​​​ 為例,滿足條件時會建立 ​

​placeholder​

​​ 元素(防止頁面抖動),同時讓 ​

​header​

​​ 定位為 ​

​fixed​

​。

右邊是 ​

​Chrome Dev-Tools​

​​ 的 ​

​layers​

​​ 面闆,藍色部分為生成的 ​

​placeholder​

​。

兩種方案的火焰圖對比(為了放大效果,我把 ​

​cpu​

​ 調慢了 6 倍)

CSS 方案

使用 ​

​CSS Sticky​

​​,工作都交給 ​

​GPU​

​​ 了,不占用 ​

​JS​

​ 主線程的資源,在移動端上異常流暢。

React Sticky

由于需要在 ​

​scroll event​

​​ 回調中不斷調用 ​

​getBoundingClientRect​

​​,而 ​

​getBoundingClientRect​

​ 又會觸發頁面重排重繪,稍不留神就掉幀卡頓。僅僅為了實作這個效果(頁面上沒有其他内容)大動幹戈成本效益很低。

結論是:實作 ​

​Sticky​

​​ 效果,優先選擇 ​

​CSS Sticky​

了解上的偏差

1. 隻在 Containing Block 内有效。

修改​​例子​​​,用一個 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​

​ 把它拖走。

​​代碼看這裡​​

2. Overflow 會影響 Sticky

修改​​例子​​​中的代碼,給 ​

​#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​

​ 。

相關連結

  • ​​uxdesign.cc/position-st…​​
  • ​​codyhouse.co/ds/componen…​​
  • ​​medium.com/@elad/css-p…​​
  • ​​developer.mozilla.org/zh-CN/docs/…​​
  • ​​stackoverflow.com/questions/4…​​