最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

原生JavaScript實現無限滾動加載效果

 更新時間:2026年04月22日 08:41:25   作者:叫我一聲阿雷吧  
無限滾動加載(Infinite?Scroll)是現代?Web?應用最主流的列表加載方式,替代傳統分頁,大幅提升用戶瀏覽體驗,下面小編就和大家詳細介紹一下如何使用原生JavaScript實現無限滾動加載效果吧

一、功能背景與應用場景

無限滾動加載(Infinite Scroll)是現代 Web 應用最主流的列表加載方式,替代傳統分頁,大幅提升用戶瀏覽體驗,常見場景:

  • 商品列表頁、推薦流
  • 評論區(qū)、消息列表
  • 文章列表、動態(tài)廣場
  • 后臺數據表格、日志列表

基礎版本容易出現:重復請求、加載閃爍、滾動抖動、結束狀態(tài)不顯示、性能差等問題。本文實現企業(yè)級穩(wěn)定版,解決所有常見坑點。

二、核心實現效果

滾動到底部自動加載數據

防重復請求(加鎖機制)

加載中狀態(tài)提示(Loading)

無數據 / 全部加載完畢狀態(tài)提示

滾動監(jiān)聽節(jié)流優(yōu)化

列表渲染無閃爍、無抖動

支持異步接口模擬(可直接對接后端)

響應式適配,移動端 / PC 端通用

代碼模塊化、可配置、易擴展

三、完整可運行源碼(直接復制發(fā)布)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>原生JS實現無限滾動加載(下拉加載更多)</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet"  rel="external nofollow" >
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: "Microsoft YaHei", sans-serif;
        }
        body {
            background-color: #f5f7fa;
            padding: 20px;
        }
        .container {
            max-width: 800px;
            margin: 0 auto;
        }
        .title {
            font-size: 24px;
            color: #2c3e50;
            margin-bottom: 20px;
            text-align: center;
        }
        /* 列表容器 */
        .list-container {
            display: flex;
            flex-direction: column;
            gap: 15px;
            margin-bottom: 30px;
            min-height: 300px;
        }
        /* 列表項 */
        .list-item {
            background: #fff;
            padding: 20px;
            border-radius: 10px;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
            transition: transform 0.2s;
        }
        .list-item:hover {
            transform: translateY(-2px);
        }
        .list-item h3 {
            font-size: 18px;
            color: #2f3542;
            margin-bottom: 8px;
        }
        .list-item p {
            font-size: 14px;
            color: #57606f;
            line-height: 1.6;
        }
        /* 加載狀態(tài) */
        .load-status {
            text-align: center;
            padding: 15px 0;
            font-size: 14px;
            color: #666;
            display: none;
        }
        .load-status.active {
            display: block;
        }
        .loading-icon {
            display: inline-block;
            animation: rotate 1s linear infinite;
            margin-right: 6px;
        }
        @keyframes rotate {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }
        /* 無更多數據 */
        .no-more {
            color: #999;
            padding: 10px 0;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1 class="title">無限滾動加載演示</h1>
        <div class="list-container" id="listContainer"></div>
        <div class="load-status" id="loadStatus">
            <i class="fas fa-spinner loading-icon"></i>
            <span>正在加載更多數據...</span>
        </div>
        <div class="load-status no-more" id="noMore">已加載全部數據</div>
    </div>
    <script>
        // ====================== 配置項 ======================
        const CONFIG = {
            pageSize: 8,          // 每頁條數
            threshold: 150,       // 距離底部多少像素觸發(fā)加載
            throttleDelay: 150,   // 滾動節(jié)流時間
            totalData: 40         // 模擬總數據量
        };
        // ====================== 狀態(tài)管理 ======================
        let currentPage = 1;
        let isLoading = false;    // 防重復請求鎖
        let isEnd = false;        // 是否全部加載完畢
        // DOM
        const listContainer = document.getElementById('listContainer');
        const loadStatus = document.getElementById('loadStatus');
        const noMore = document.getElementById('noMore');
        // ====================== 初始化 ======================
        function init() {
            loadData();
            window.addEventListener('scroll', throttle(handleScroll, CONFIG.throttleDelay));
        }
        // ====================== 滾動監(jiān)聽(觸底判斷) ======================
        function handleScroll() {
            if (isLoading || isEnd) return;
            const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
            const clientHeight = document.documentElement.clientHeight;
            const scrollHeight = document.documentElement.scrollHeight;
            // 觸底判斷
            if (scrollTop + clientHeight + CONFIG.threshold >= scrollHeight) {
                currentPage++;
                loadData();
            }
        }
        // ====================== 加載數據(模擬接口) ======================
        function loadData() {
            isLoading = true;
            loadStatus.classList.add('active');
            // 模擬請求延遲
            setTimeout(() => {
                const start = (currentPage - 1) * CONFIG.pageSize;
                const end = currentPage * CONFIG.pageSize;
                // 構造數據
                const list = [];
                for (let i = start; i < end && i < CONFIG.totalData; i++) {
                    list.push({
                        id: i + 1,
                        title: `這是第 ${i + 1} 條內容`,
                        content: '原生JS實現無限滾動加載,支持防重復請求、滾動節(jié)流、狀態(tài)管理,企業(yè)級穩(wěn)定方案。'
                    });
                }
                // 渲染
                renderList(list);
                // 判斷是否加載完畢
                if (end >= CONFIG.totalData) {
                    isEnd = true;
                    loadStatus.classList.remove('active');
                    noMore.classList.add('active');
                } else {
                    isLoading = false;
                    loadStatus.classList.remove('active');
                }
            }, 800);
        }
        // ====================== 渲染列表 ======================
        function renderList(data) {
            data.forEach(item => {
                const div = document.createElement('div');
                div.className = 'list-item';
                div.innerHTML = `
                    <h3>${item.title}</h3>
                    <p>${item.content}</p>
                `;
                listContainer.appendChild(div);
            });
        }
        // ====================== 節(jié)流函數 ======================
        function throttle(fn, delay) {
            let lastTime = 0;
            return function (...args) {
                const now = Date.now();
                if (now - lastTime >= delay) {
                    lastTime = now;
                    fn.apply(this, args);
                }
            };
        }
        // 啟動
        init();
    </script>
</body>
</html>

四、核心技術講解

1. 觸底判斷公式(最關鍵)

scrollTop + clientHeight + threshold >= scrollHeight
  • scrollTop:滾動條滾動距離
  • clientHeight:可視窗口高度
  • threshold:距離底部提前觸發(fā)距離
  • scrollHeight:頁面總高度

只要滿足公式,就判定即將觸底,開始加載下一頁。

2. 防重復請求(企業(yè)必備)

使用isLoading 鎖

  • 開始請求 → isLoading = true
  • 結束請求 → isLoading = false
  • 滾動時判斷鎖狀態(tài),避免多次觸發(fā)

3. 性能優(yōu)化:滾動節(jié)流

滾動事件觸發(fā)頻率極高,必須使用節(jié)流

  • 每 150ms 只執(zhí)行一次
  • 大幅降低瀏覽器性能消耗
  • 避免頁面卡頓

4. 狀態(tài)管理規(guī)范

  • 加載中 → 顯示 Loading
  • 無數據 → 顯示 “已加載全部”
  • 結束后不再監(jiān)聽滾動
  • 全程無閃爍、無抖動

5. 擴展性極強

可直接對接真實接口:

  • 替換 setTimeoutfetch/axios
  • 后端返回總條數即可判斷是否結束

五、可擴展高級功能(文章加分項)

  1. 錯誤重試:加載失敗顯示重試按鈕
  2. 下拉刷新:配合 touch 事件實現下拉刷新
  3. 緩存機制:使用 localStorage 緩存列表
  4. 虛擬列表:支持 10w+ 數據不卡頓(超高分亮點)
  5. 淡入動畫:加載新項目時添加動畫
  6. 定位滾動:返回后保持上次位置

六、適用場景

  • 商品列表
  • 評論列表
  • 消息流
  • 后臺管理表格
  • 文章 / 動態(tài)列表

所有長列表業(yè)務都能直接使用。

七、方法補充

原生 JavaScript 實現無限滾動加載是一種常見的列表分頁技術,核心是監(jiān)聽滾動事件,當用戶滾動到頁面底部(或接近底部)時,自動加載下一頁數據并追加到列表中。以下是一個完整的實現方案,包含核心原理、代碼示例及優(yōu)化技巧。

核心原理

  • 監(jiān)聽滾動事件:使用 window 或特定容器的 scroll 事件。
  • 計算滾動位置:判斷 scrollTop + clientHeight >= scrollHeight - threshold(threshold 為閾值,如 200px)。
  • 觸發(fā)加載:滿足條件時,調用加載函數,請求新數據。
  • 追加內容:將新數據渲染到頁面列表末尾。
  • 狀態(tài)控制:使用 isLoading 標志防止重復請求,使用 hasMore 標志表示是否還有更多數據。

實現代碼

HTML 結構

<div id="app">
  <ul id="list" class="list"></ul>
  <div id="loading" class="loading hidden">加載中...</div>
  <div id="no-more" class="no-more hidden">沒有更多數據了</div>
</div>

CSS 樣式(簡單示例)

.list {
  list-style: none;
  padding: 0;
  margin: 0;
}
.list li {
  padding: 12px;
  border-bottom: 1px solid #eee;
}
.loading, .no-more {
  text-align: center;
  padding: 10px;
  color: #999;
}
.hidden {
  display: none;
}

JavaScript 實現

// 模擬異步數據請求
function fetchData(page, pageSize = 10) {
  return new Promise((resolve) => {
    setTimeout(() => {
      const start = (page - 1) * pageSize;
      const items = Array.from({ length: pageSize }, (_, i) => ({
        id: start + i + 1,
        content: `條目 ${start + i + 1}`
      }));
      // 模擬總共 50 條數據
      const hasMore = start + pageSize < 50;
      resolve({ items, hasMore });
    }, 500);
  });
}
class InfiniteScroll {
  constructor(options) {
    this.container = options.container;      // 滾動容器,默認為 window
    this.listEl = options.listEl;            // 列表容器元素
    this.loadingEl = options.loadingEl;      // 加載提示元素
    this.noMoreEl = options.noMoreEl;        // 無更多數據提示元素
    this.fetchData = options.fetchData;      // 獲取數據的函數
    this.page = 1;
    this.pageSize = options.pageSize || 10;
    this.isLoading = false;
    this.hasMore = true;
    this.threshold = options.threshold || 200; // 距離底部多少像素時觸發(fā)
    this.init();
  }
  init() {
    this.loadMore = this.loadMore.bind(this);
    // 綁定滾動事件(使用節(jié)流優(yōu)化)
    this.scrollHandler = this.throttle(this.checkScroll.bind(this), 200);
    this.container.addEventListener('scroll', this.scrollHandler);
    if (this.container === window) {
      window.addEventListener('scroll', this.scrollHandler);
    }
    // 初始加載第一頁
    this.loadMore();
  }
  // 檢查是否需要加載
  checkScroll() {
    if (!this.hasMore || this.isLoading) return;
    let scrollTop, clientHeight, scrollHeight;
    if (this.container === window) {
      scrollTop = window.pageYOffset || document.documentElement.scrollTop;
      clientHeight = document.documentElement.clientHeight;
      scrollHeight = document.documentElement.scrollHeight;
    } else {
      scrollTop = this.container.scrollTop;
      clientHeight = this.container.clientHeight;
      scrollHeight = this.container.scrollHeight;
    }
    if (scrollTop + clientHeight >= scrollHeight - this.threshold) {
      this.loadMore();
    }
  }
  // 加載更多數據
  async loadMore() {
    if (!this.hasMore || this.isLoading) return;
    this.isLoading = true;
    this.showLoading(true);
    try {
      const { items, hasMore } = await this.fetchData(this.page, this.pageSize);
      this.renderItems(items);
      this.hasMore = hasMore;
      this.page++;
      if (!this.hasMore) {
        this.showNoMore(true);
      }
    } catch (error) {
      console.error('加載失敗', error);
      // 可在此處顯示錯誤提示,并提供重試按鈕
    } finally {
      this.isLoading = false;
      this.showLoading(false);
    }
  }
  // 渲染列表項
  renderItems(items) {
    const fragment = document.createDocumentFragment();
    items.forEach(item => {
      const li = document.createElement('li');
      li.textContent = item.content;
      fragment.appendChild(li);
    });
    this.listEl.appendChild(fragment);
  }
  // 顯示/隱藏加載提示
  showLoading(show) {
    if (this.loadingEl) {
      this.loadingEl.classList.toggle('hidden', !show);
    }
  }
  // 顯示/隱藏無更多數據提示
  showNoMore(show) {
    if (this.noMoreEl) {
      this.noMoreEl.classList.toggle('hidden', !show);
    }
  }
  // 節(jié)流函數
  throttle(fn, delay) {
    let last = 0;
    return function(...args) {
      const now = Date.now();
      if (now - last > delay) {
        last = now;
        fn.apply(this, args);
      }
    };
  }
  // 銷毀,移除事件監(jiān)聽
  destroy() {
    if (this.container === window) {
      window.removeEventListener('scroll', this.scrollHandler);
    } else {
      this.container.removeEventListener('scroll', this.scrollHandler);
    }
  }
}
// 使用示例
const infiniteScroll = new InfiniteScroll({
  container: window,                  // 滾動容器
  listEl: document.getElementById('list'),
  loadingEl: document.getElementById('loading'),
  noMoreEl: document.getElementById('no-more'),
  fetchData: fetchData,              // 自定義數據獲取函數
  pageSize: 10,
  threshold: 200
});

八、總結

本文實現的無限滾動加載組件是前端高頻面試題 + 企業(yè)必備功能。特點:原生 JS、無依賴、防重復請求、性能高、狀態(tài)完整、無閃爍。

以上就是原生JavaScript實現無限滾動加載效果的詳細內容,更多關于JavaScript無限滾動加載的資料請關注腳本之家其它相關文章!

相關文章

  • 禁止js文件緩存的代碼

    禁止js文件緩存的代碼

    禁止js文件緩存問題是我一直遇到的大問題,終于找到了一個比較好的辦法
    2010-04-04
  • JavaScript中使用Object.create()創(chuàng)建對象介紹

    JavaScript中使用Object.create()創(chuàng)建對象介紹

    這篇文章主要介紹了JavaScript中使用Object.create()創(chuàng)建對象介紹,本文先是講解了語法,然后給出了創(chuàng)建實例,需要的朋友可以參考下
    2014-12-12
  • js對象數組按屬性快速排序

    js對象數組按屬性快速排序

    前一篇《關于selector性能比賽》中提到,目測覺得在$("div,p,a")這樣有逗號時,sizzle耗時異常(600多個元素,花了200ms),說是它可能沒有優(yōu)化ie下的排序。
    2011-01-01
  • JavaScript插件化開發(fā)教程 (三)

    JavaScript插件化開發(fā)教程 (三)

    前面我們學習了jQuery的方式開發(fā)插件,講訴的都是些基礎的理論知識,今天開始,我們就來實戰(zhàn)一下,學習開發(fā)自己的插件庫。
    2015-01-01
  • javascript中字體浮動效果的簡單實例演示

    javascript中字體浮動效果的簡單實例演示

    這篇文章主要介紹了javascript中字體浮動效果的簡單實例演示,在一些網站上經常遇到當鼠標移導航欄的時候,能夠使其彈出下拉選項,現在就演示一下這種功能,感興趣的小伙伴們可以參考一下
    2015-11-11
  • 詳解JavaScript函數對象

    詳解JavaScript函數對象

    函數是由事件驅動的或者當它被調用時執(zhí)行的可重復使用的代碼塊,JavaScript 中的所有事物都是對象:字符串、數值、數組、函數,下面通過本文給大家介紹JavaScript函數對象,感興趣的朋友一起學習吧
    2015-11-11
  • js實現樓層效果的簡單實例

    js實現樓層效果的簡單實例

    下面小編就為大家?guī)硪黄猨s實現樓層效果的簡單實例。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-07-07
  • JS trim去空格的最佳實踐

    JS trim去空格的最佳實踐

    學習框架的我,又來了。看到 String 對象擴展這一部分,對 trim() 這個經常被來來說的方法比較感興趣
    2011-10-10
  • 微信小程序云開發(fā)實現分頁刷新獲取數據

    微信小程序云開發(fā)實現分頁刷新獲取數據

    這篇文章主要為大家詳細介紹了微信小程序云開發(fā)實現分頁刷新獲取數據,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • 模仿password輸入框的實現代碼

    模仿password輸入框的實現代碼

    下面小編就為大家?guī)硪黄7聀assword輸入框的實現代碼。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-06-06

最新評論

新乡市| 天水市| 景谷| 库伦旗| 晋中市| 阿瓦提县| 买车| 女性| 隆安县| 陵水| 阳谷县| 永昌县| 高淳县| 高平市| 莫力| 炉霍县| 英德市| 江达县| 同德县| 兴仁县| 交口县| 亚东县| 定结县| 兴化市| 赣州市| 仪陇县| 湟中县| 宁津县| 潼关县| 修水县| 浪卡子县| 额尔古纳市| 宝清县| 息烽县| 得荣县| 旬邑县| 桂阳县| 安泽县| 崇明县| 雷波县| 邵阳市|