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

NO IMAGE

首發於我的 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.

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

相關文章

如何使用自簽名CA和證書來保護個人在公網上的內容

如何讓你的React『變慢』?探析ArrayDiff的一些邊角特性

可控組件?不可控組件?讓我們來討論一下下~

嚐鮮用ReactHook+Parcel構建真心話大冒險簡單頁面