在本教程中,我們將看到一個使用無限滾動方法分解頁面內容的簡單實現。我們將使用 html、css 和 vanilla JAVAScript 來構建無限滾動功能的高性能且可訪問的版本。
什么是無限滾動?
無限滾動是一種用于在用戶滾動到頁面末尾時在頁面上動態加載更多內容的功能。
無限滾動的概念用于以一種用戶感覺“無縫”的方式從服務器加載數據,但不會因為一次請求太多數據而使服務器過載。
在之前的教程中,我們實現了分頁功能,它允許我們將內容分解為稱為頁面的可導航部分。本教程將使用類似的實現。
原生 JavaScript 的好處
使用 JavaScript 的一個顯著好處是我們的實現與框架無關,即它不依賴于任何框架,因此可以對其進行修改以在所有框架上工作。
此外,由于我們自己構建功能而不依賴于插件,因此我們可以確保實現是輕量級的并且完全適合我們的需求。
這是最終產品的外觀,滾動到筆的底部以加載更多內容:
1.用 HTML 標記
我們將從在頁面上放置卡片的容器開始。我們將使用 JavaScript 將卡片添加到容器中,因此 div 將為空。
<div id="card-container"></div>
我們還有一個加載器div,用于在添加下一批卡片之前顯示動畫,以及div用于顯示卡片數量和卡片總數的卡片操作。
<div id="loader">
<div class="skeleton-card"></div>
<div class="skeleton-card"></div>
<div class="skeleton-card"></div>
</div>
<div class="card-actions">
<span>Showing
<span id="card-count"></span> of
<span id="card-total"></span> cards
</span>
</div>
加載器和卡片計數 div 的外觀
2.使用 CSS 進行樣式設置
我們將添加到 card-container div 中的卡片將有一個類名“card”。
#card-container {
display: flex;
flex-wrap: wrap;
}
.card {
height: 55vh;
width: calc((100% / 3) - 16px);
margin: 8px;
border-radius: 3px;
transition: all 200ms ease-in-out;
display: flex;
align-items: center;
justify-content: center;
}
.card:hover {
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}
.card-actions {
margin: 8px;
padding: 16px 0;
display: flex;
justify-content: space-between;
align-items: center;
}
我們還將通過動畫::after偽選擇器為加載器 div 中的骨架卡創建加載動畫:
#loader {
display: flex;
}
.skeleton-card {
height: 55vh;
width: calc((100% / 3) - 16px);
margin: 8px;
border-radius: 3px;
transition: all 200ms ease-in-out;
position: relative;
background-color: #eaeaea;
}
.skeleton-card::after {
content: "";
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
transform: translateX(-100%);
background-image: linear-gradient(90deg, rgba(255, 255, 255, 0) 0, rgba(255, 255, 255, 0.2) 20%, rgba(255, 255, 255, 0.5) 60%, rgba(255, 255, 255, 0));
animation: load 1s infinite;
}
@keyframes load {
100% {
transform: translateX(100%);
}
}
無障礙樣式
每當我們在網頁上包含動畫時,重要的是要考慮可訪問性的影響。有些用戶可能更喜歡根本沒有動畫,我們可以通過使用媒體規則在樣式中考慮這種偏好,prefers-reduced-motion
@media screen and (prefers-reduced-motion: reduce) {
.skeleton-card::after {
animation: none;
}
}
3.JavaScript 的功能
讓我們分解無限滾動背后的邏輯。
- 定義要在頁面上加載的內容的限制。
- 檢測用戶何時到達內容容器的末尾。
- 到達容器末尾后加載更多內容。
- 如果沒有更多內容要加載,請停止無限滾動。
定義常量
首先,讓我們從 DOM 中獲取我們需要的所有元素:
const cardContainer = document.getElementById("card-container");
const cardCountElem = document.getElementById("card-count");
const cardTotalElem = document.getElementById("card-total");
const loader = document.getElementById("loader");
現在我們需要定義我們的全局變量。
我們需要一個要添加到頁面的最大卡片數量的值。如果您從服務器獲取數據,則此值是服務器響應的長度。讓我們初始化一個 99 的卡片限制。
const cardLimit = 99;
cardTotalElem 是用于在頁面上顯示最大卡片數量的元素,因此我們可以將 設置為innerHTML值cardLimit;
cardTotalElem.innerHTML = cardLimit;
然后我們將定義一個變量來表示要增加頁面的卡片數量:
const cardIncrease = 9;
我們想知道我們將擁有多少“頁面”,即我們可以增加多少次內容,直到達到最大限制。例如,使用我們定義的cardLimit和cardIncrease變量,我們可以將內容增加 10 倍(假設我們已經加載了前 9 個元素),直到達到限制。我們將通過除以 來做到這cardLimit一點cardIncrease。
const pageCount = Math.ceil(cardLimit / cardIncrease);
然后我們將定義一個值來確定我們在哪個頁面上:
let currentPage = 1;
創建新卡
現在我們有了所有的常量,讓我們創建一個函數來向卡片容器中添加一張新卡片。我們將innerHTML卡片的 設置為索引值,這樣我們就可以跟蹤我們正在添加的卡片數量。
此演示中的一個有趣功能是每張卡片都有隨機生成的背景顏色。
- 如何使用 JavaScript 生成隨機背景顏色
const getRandomColor = () => {
const h = Math.floor(Math.random() * 360);
return `hsl(${h}deg, 90%, 85%)`;
};
const createCard = (index) => {
const card = document.createElement("div");
card.className = "card";
card.innerHTML = index;
card.style.backgroundColor = getRandomColor();
cardContainer.AppendChild(card);
};
將卡片添加到容器中
現在我們將使用與分頁教程類似的功能將卡片添加到容器中。
首先,確定要添加到頁面的卡片范圍。該addCards函數將接受一個pageIndex參數,該參數將更新全局currentPage值。如果我們在第 1 頁,我們將添加卡片 1 到 9。如果我們在第 2 頁,我們將添加卡片 10 到 18,依此類推。
我們可以在數學上將其定義為:
const addCards = (pageIndex) => {
currentPage = pageIndex;
const startRange = (pageIndex - 1) * cardIncrease;
const endRange = pageIndex * cardIncrease;
for (let i = startRange + 1; i <= currRange; i++) {
createCard(i);
}
};
在這個函數中,我們的開始范圍總是比我們試圖獲得的值小一(即在第 1 頁,開始范圍是 0,在第 2 頁,開始范圍是 9)所以我們將考慮這一點通過將我們的 for 循環索引的值設置為startRange + 1.
檢測何時達到卡限制
我們必須注意的一個限制是endRange數量。如果我們在最后一頁,我們希望結束范圍與cardLimit. 例如,如果我們有cardLimit75 和cardIncrease10 的 a 并且我們在第 8 頁,我們的起始索引將是 70,我們的endRange值應該是 75。
我們將修改我們的addCards函數來解決這個問題:
const addCards = (pageIndex) => {
currentPage = pageIndex;
const startRange = (pageIndex - 1) * cardIncrease;
const endRange = currentPage == pageCount ? cardLimit : pageIndex * cardIncrease;
for (let i = startRange + 1; i <= endRange; i++) {
createCard(i);
}
};
我們的演示還包括一個cardTotal元素,該元素顯示頁面上當前顯示的卡片數量,因此我們innerHTML將此元素的 設置為結束范圍。
const addCards = (pageIndex) => {
currentPage = pageIndex;
const startRange = (pageIndex - 1) * cardIncrease;
const endRange = currentPage == pageCount ? cardLimit : pageIndex * cardIncrease;
cardCountElem.innerHTML = endRange;
for (let i = startRange + 1; i <= endRange; i++) {
createCard(i);
}
};
加載初始卡片
我們已經定義了將卡片添加到容器的功能,因此我們將包含一個window.onload函數來設置要添加到頁面的初始卡片。
window.onload = function () {
addCards(currentPage);
};
處理無限滾動
currentPage當我們到達頁面末尾時,我們將通過增加向容器添加新卡片的數量來處理無限滾動。innerHeight我們可以通過將窗口的 值添加到滾動值pageYOffset并將其與offsetHeight作為頁面總高度的文檔進行比較來檢測何時到達頁面末尾。
這是它的外觀的視覺表示:
一旦我們到達頁面的末尾,我們想通過調用addCardscurrentPage + 1 的函數來加載一個新頁面。
const handleInfiniteScroll = () => {
const endOfPage = window.innerHeight + window.pageYOffset >= document.body.offsetHeight;
if (endOfPage) {
addCards(currentPage + 1);
}
};
然后我們為窗口滾動創建一個事件監聽器,并將上面的函數傳遞給它:
window.addEventListener("scroll", handleInfiniteScroll);
性能優化
由于我們正在使用滾動事件偵聽器,因此限制調用次數對我們網頁的性能是有益的。我們可以使用節流功能減慢調用次數。
- 如何使用 JavaScript 實現 Debounce 和 Throttle杰邁瑪·阿布2021 年 5 月 3 日
我們將這樣定義我們的節流函數:
var throttleTimer;
const throttle = (callback, time) => {
if (throttleTimer) return;
throttleTimer = true;
setTimeout(() => {
callback();
throttleTimer = false;
}, time);
};
然后我們將油門函數傳遞給handleInfiniteScroll函數
const handleInfiniteScroll = () => {
throttle(() => {
const endOfPage =
window.innerHeight + window.pageYOffset >= document.body.offsetHeight;
if (endOfPage) {
addCards(currentPage + 1);
}
}, 1000);
};
停止無限滾動
至此,我們已經設置了函數,以便在到達頁面末尾時添加更多內容。現在,讓我們確保我們的函數在沒有要添加的內容時停止運行,即cardLimit到達時。
首先,讓我們定義我們的removeInfiniteScroll函數。在此函數中,我們將從handleInfiniteScroll滾動事件偵聽器中刪除該函數,并刪除加載器 div。
const removeInfiniteScroll = () => {
loader.remove();
window.removeEventListener("scroll", handleInfiniteScroll);
};
現在我們將修改我們handleInfiniteScroll以考慮是否沒有更多內容要添加,即我們在內容的最后一頁。
const handleInfiniteScroll = () => {
throttle(() => {
const endOfPage =
window.innerHeight + window.pageYOffset >= document.body.offsetHeight;
if (endOfPage) {
addCards(currentPage + 1);
}
if (currentPage === pageCount) {
removeInfiniteScroll();
}
}, 1000);
};
結論
我們終于得到它了!我們已經構建了無限滾動功能的高性能且可訪問的實現。