【後知後覺系列】css position: sticky 屬性以及某些場景的使用

XGHeaven發表於2019-02-27

首發於我的 Blog

不知何時,曾經我們認為的東西便會被打破,如果我們不堅持著去學習,那麼我們終將會被社會所淘汰。於是我決定寫《後知後覺系列》來記錄一下我曾經跟不上的知識和關鍵點,內容不一定複雜,內容含量不一定高,也許別人已經寫過一個一樣的教程了,但是希望你能從我的筆記中獲取你認為重要的東西,在紛繁複雜的工作中留下一個真正極客的世界,希望某一天這些東西都能夠運用到工作當中。——XGHeaven

記得先看一下目錄,找到你喜歡好奇的內容去針對性閱讀,畢竟我不是來寫教程的。

position: sticky 這究竟是一個什麼鬼?

最近公司在用 Regular 封裝一個表格元件,需要實現固定表頭的功能。這個是幾乎所有的元件庫都會實現的一個效果,所以實現方式有很多種:

  1. 因為 thead/tr 的 position 屬性是無效的,所以需要單獨用 div 建立一個表頭。然後設定這個表頭的 position: absolute,同時 top: 0。同時這種模式下,需要使用者指定每一列的寬度,保證自制的表頭和下面原生的表格一一對應起來。如果不指定的話,也可以等待 dom 渲染完成之後,再測量寬度。比如 Ant Design 就是使用的這種方式。
  2. 因為上面那種方案的難點在於無法很好的保證自制表頭和原生表格寬度的一致性,所以我們組的大佬提出了使用原生 thead,監聽 scroll 事件,設定 transform 屬性使得表頭進行偏移,從而實現 fixHeader 的問題,這種方式解決了第一個的問題,但是需要手動監聽 scroll 事件,在快速滾動的情況下,可能會有一定的效能問題。而且不夠優雅。如果後面的表格內容中有 position: relative 的元素,會覆蓋到表頭。

不管是哪種方式,我總感覺不是很完美,於是我就在思考,除了手動更新的方式,難道就沒有一些比較好的方式去做。然後我就去翻看了 github 的固定表頭的方式,頓時豁然開朗。於是就延伸出了這篇文章,position: sticky 屬性。

Pay Attention:後面所講的內容就不怎麼和表格固定表頭相關,如果你對錶格固定表頭或者固定列有一定問題,可以檢視網易考拉的這篇文章 《一起來聊聊table元件的固定列》

當第一眼看到這個熟悉的時候,第一句話就是“我 CA”,這 TMD 是什麼鬼屬性,position 什麼時候有了這個屬性。於是去看了 MDN 的介紹,可以理解為,這個屬性是實現固定頂部最簡單的實現方式

他其實是一種 position:relativeposition: fixed 的結合體,一定要配合 top/right/bottom/left 的屬性一起才有作用,設定對應方向的最小值。當大於最小值的時候,他就像 relative 一樣,作為文件流的一部分,並且 top/right/bottom/left 屬性也會失效。否則當小於設定的值的時候表現的像 fixed,只不過這個 fixed 不再現對於視窗,而是相對於最近的可滾動塊級元素。

如果你看過其他關於 sticky 的文章,大部分都會以黏貼的意思來解釋他,那麼很明顯,確實也是這個意思,如果你覺得看了其他教程能夠清楚的話,那麼可以不用看我這篇了,如果你沒看懂的話,可以來我這裡看看。

廢話少說,我們先來看一下如何正確使用 sticky。

正確的使用姿勢

以下的程式碼預覽請使用最新 Chrome 檢視,或者支援 position: sticky 的瀏覽器檢視。部分網站不支援 iframe,可以去我的 Blog 檢視

  1. position: sticky 只相對於第一個有滾動的父級塊元素(scrolling mechanism,通過 overflow 設定為 overflow/scroll/auto/overlay 的元素),而不是父級塊元素。

    See the Pen position sticky 相對於最外面的可滾動父級 by Bradley Xu (@xgheaven) on CodePen.

  2. position: sticky 只有當設定對應的方向(top/right/bottom/left),才會有作用,並且可以互相疊加,可以同時設定四個方向。

  3. 即使設定了 position: sticky,也只能顯示在父級塊元素的內容區域,他無法超出這個區域,除非你設定了負數的值。

  4. position: sticky 並不會觸發 BFC,簡單來講就是計算高度的時候不會計算 float 元素。

  5. 當設定了 position: sticky 之後,內部的定位會相對於這個元素

    See the Pen position sticky 內部絕對定位相對於這個元素 by Bradley Xu (@xgheaven) on CodePen.

  6. 雖然 position: sticky 表現的像 relative 或者 fixed,所以也是可以通過 z-index 設定他們的層級。當這個元素的後面的兄弟節點會覆蓋這個元素的時候,可以通過 z-index 調節層級。

    See the Pen position: sticky 通過 z-index 調節層級 by Bradley Xu (@xgheaven) on CodePen.

當你懂了這幾個之後,其實這個屬性就用起來就很簡單了。

舉個例子 – 通訊錄列表頭部

no code no bb,直接上程式碼。

See the Pen position sticky 通訊錄 Demo by Bradley Xu (@xgheaven) on CodePen.

擴充思考

如何檢測是否已經被固定?

最常見的需求就是,當還在文件流當中的時候,正常顯示,但是當固定住的時候,新增一些陰影或者修改高度等操作。要想實現這個效果,第一反應可能就是手動監聽 scroll 事件,判斷位置,這當然是沒有問題的,但是隨之而來的確實效能的損耗。

最好的方式是使用 IntersectionObserver,這是一個可以監聽一個元素是否顯示在視窗之內的 API,具體內容見阮老師的《IntersectionObserver API 使用教程》。基本原理就是在一段滾動的頭部和尾部分別新增兩個崗哨,然後通過判斷這兩個崗哨的出現和消失的時機,來判斷元素是否已經被固定。

例子詳見此處

那能不能實現表格頭/列固定呢?

理想很豐滿,顯示很骨感,因為 thead/tbody 對 position 無愛,所以也就不支援 sticky 屬性,所以我們還是要單獨建立一個頭部。

後來經過網友提醒,自己又去研究了一下,發現還是有辦法做到固定表頭和列的。

首先針對 Firefox,它本身就支援 thead/tbody 的 position 屬性,所以可以直接通過對 thead/tbody 設定 position 來實現。而對於 Chrome 瀏覽器來講,可以通過設定 thead 內的 th 來實現。具體見 Demo.

See the Pen position sticky 通過設定 td 來實現固定表頭 by Bradley Xu (@xgheaven) on CodePen.

然後好像就沒有了,謝謝觀看水水的《後知後覺系列》

相關文章