- 原文地址:Applying Styles Based on the User Scroll Position with Smart CSS
- 原文作者:Rik Schennink
- 譯文出自:掘金翻譯計劃
- 本文永久連結:github.com/xitu/gold-m…
- 譯者:jerryOnlyZRJ
- 校對者:ScDadaguo
通過將當前滾動偏移量新增到到 html
元素的屬性上,我們可以根據當前滾動位置設定頁面上的元素樣式。我們可以使用它來構建一個浮動在頁面頂部的導航元件。
這是我們將使用的 HTML,<header>
元件是我們希望當我們向下滾動時,始終浮動在頁面頂部的一個元件。
<header>I'm the page header</header>
<p>Lot's of content here...</p>
<p>More beautiful content...</p>
<p>Content...</p>
複製程式碼
首先,我們將監聽 document
上的 'scroll'
事件,並且每次使用者滾動時我們都會取出當前的 scrollY
值。
document.addEventListener('scroll', () => {
document.documentElement.dataset.scroll = window.scrollY;
});
複製程式碼
我們將滾動位置儲存在 html
元素的資料屬性中。如果您使用開發工具檢視 DOM,它將如下所示:<html data-scroll="0">
現在我們可以使用此屬性來設定頁面上的元素樣式。
/* 保證 header標籤始終高於 3em */
header {
min-height: 3em;
width: 100%;
background-color: #fff;
}
/* 在頁面頂部保留與 header 的 min-height 相同的高度 */
html:not([data-scroll='0']) body {
padding-top: 3em;
}
/* 將 header 標籤切換成 fixed 定位模式,並且將它固定在頁面頂部 */
html:not([data-scroll='0']) header {
position: fixed;
top: 0;
z-index: 1;
/* box-shadow 屬效能夠增強浮動的效果 */
box-shadow: 0 0 .5em rgba(0, 0, 0, .5);
}
複製程式碼
基本上就是這樣,當使用者向下滾動時,header 標籤將自動從頁面中分離並浮動在內容之上。JavaScript 程式碼並不關心這一點,它的任務就是將滾動偏移量放在資料屬性中。這很完美,因為 JavaScript 和 CSS 之間沒有緊密耦合。
但仍有一些可以改進的地方,主要是在效能方面。
首先,我們必須修改 JavaScript 指令碼,以適應頁面載入時滾動位置不在頂部的情況。在這樣的情況下,header 標籤將呈現錯誤的樣式。
頁面載入時,我們必須快速獲取當前的滾動偏移量,這樣確保了我們始終與當前的頁面的狀態同步。
// 讀出當前頁面的滾動位置並將其存入 document 的 data 屬性中
// 因此我們就可以在我們的樣式表中使用它
const storeScroll = () => {
document.documentElement.dataset.scroll = window.scrollY;
}
// 監聽滾動事件
document.addEventListener('scroll', storeScroll);
// 第一次開啟頁面時就更新滾動位置
storeScroll();
複製程式碼
接下來我們將看一些效能方面改進。如果我們想要獲取 scrollY
滾動位置,瀏覽器將必須計算頁面上每個元素的位置,以確保它返回正確的位置。如果我們不強制它每次滾動都取值才是最好的做法。
要做到這一點,我們需要一個 debounce(防抖動)方法,這個方法會將我們的取值請求加入一個佇列中,在瀏覽器準備好繪製下一幀之前都不會重新取值,此時它已經計算出了頁面上所有元素的位置,所以它不會不斷重複相同的工作。
// 防抖動函式接受一個我們自定義的函式作為引數
const debounce = (fn) => {
// 這包含了對 requestAnimationFrame 的引用,所以我們可以在我們希望的任何時候停止它
let frame;
// 防抖動函式將返回一個可以接受多個引數的新函式
return (...params) => {
// 如果 frame 的值存在,那就清除對應的回撥
if (frame) {
cancelAnimationFrame(frame);
}
// 使我們的回撥在瀏覽器下一幀重新整理時執行
frame = requestAnimationFrame(() => {
// 執行我們的自定義函式並傳遞我們的引數
fn(...params);
});
}
};
// Reads out the scroll position and stores it in the data attribute
// so we can use it in our stylesheets
const storeScroll = () => {
document.documentElement.dataset.scroll = window.scrollY;
}
// Listen for new scroll events, here we debounce our `storeScroll` function
document.addEventListener('scroll', debounce(storeScroll));
// Update scroll position for first time
storeScroll();
複製程式碼
通過標記事件為 passive
狀態,我們可以告訴瀏覽器我們的滾動事件不會被觸控互動阻止(例如與谷歌地圖等外掛互動時)。這允許瀏覽器立即滾動頁面,因為它現在知道該事件不會被阻止。
document.addEventListener('scroll', debounce(storeScroll), { passive: true });
複製程式碼
解決了效能問題後,我們現在可以通過穩定的方式使用 JavaScript 將獲取的資料提供給 CSS,並可以使用它來為頁面上的元素新增樣式。
如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。
掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。