一、功能背景与应用场景
无限滚动加载(Infinite Scroll)是现代 Web 应用最主流的列表加载方式,替代传统分页,大幅提升用户浏览体验,常见场景:
- 商品列表页、推荐流
- 评论区、消息列表
- 文章列表、动态广场
- 后台数据表格、日志列表
基础版本容易出现:重复请求、加载闪烁、滚动抖动、结束状态不显示、性能差 等问题。本文实现企业级稳定版,解决所有常见坑点。
二、核心实现效果
✅ 滚动到底部自动加载数据✅ 防重复请求(加锁机制)✅ 加载中状态提示(Loading)✅ 无数据 / 全部加载完毕状态提示✅ 滚动监听节流优化 ✅ 列表渲染无闪烁、无抖动✅ 支持异步接口模拟(可直接对接后端)✅ 响应式适配,移动端 / PC 端通用✅ 代码模块化、可配置、易扩展
三、完整可运行源码(直接复制发布)
html
预览
html
<!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" href="https://cdn.bootcdn.net/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<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;
}
/* 加载状态 */
.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, // 距离底部多少像素触发加载
throttleDelay: 150, // 滚动节流时间
totalData: 40 // 模拟总数据量
};
// ====================== 状态管理 ======================
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));
}
// ====================== 滚动监听(触底判断) ======================
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实现无限滚动加载,支持防重复请求、滚动节流、状态管理,企业级稳定方案。'
});
}
// 渲染
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);
});
}
// ====================== 节流函数 ======================
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>
四、核心技术讲解(CSDN 高分必备)
1. 触底判断公式(最关键)
js
javascript
scrollTop + clientHeight + threshold >= scrollHeight
scrollTop:滚动条滚动距离clientHeight:可视窗口高度threshold:距离底部提前触发距离scrollHeight:页面总高度
只要满足公式,就判定即将触底,开始加载下一页。
2. 防重复请求(企业必备)
使用isLoading 锁:
- 开始请求 →
isLoading = true - 结束请求 →
isLoading = false - 滚动时判断锁状态,避免多次触发
3. 性能优化:滚动节流
滚动事件触发频率极高,必须使用节流:
- 每 150ms 只执行一次
- 大幅降低浏览器性能消耗
- 避免页面卡顿
4. 状态管理规范
- 加载中 → 显示 Loading
- 无数据 → 显示 "已加载全部"
- 结束后不再监听滚动
- 全程无闪烁、无抖动
5. 扩展性极强
可直接对接真实接口:
- 替换
setTimeout为fetch/axios - 后端返回总条数即可判断是否结束
五、可扩展高级功能(文章加分项)
- 错误重试:加载失败显示重试按钮
- 下拉刷新:配合 touch 事件实现下拉刷新
- 缓存机制:使用 localStorage 缓存列表
- 虚拟列表:支持 10w+ 数据不卡顿(超高分亮点)
- 淡入动画:加载新项目时添加动画
- 定位滚动:返回后保持上次位置
六、适用场景
- 商品列表
- 评论列表
- 消息流
- 后台管理表格
- 文章 / 动态列表
所有长列表业务都能直接使用。
七、总结
本文实现的无限滚动加载组件 是前端高频面试题 + 企业必备功能。特点:原生 JS、无依赖、防重复请求、性能高、状态完整、无闪烁 。代码可直接用于生产环境,也可作为前端实战案例发布到 CSDN,属于高阅读、高收藏、高互动的爆款技术文章。