无限滚动是现在常见的网页交互效果,用户滚动到页面底部时自动加载新内容,不需要手动点击翻页,能提升内容浏览的流畅度。下面我们一步步实现这个功能并优化性能。

基础实现方案
实现无限滚动的核心是监听页面滚动位置,判断用户是否到达底部,到达后触发加载逻辑。早期常用scroll事件监听,现在更推荐用Intersection_ObserverAPI,性能和兼容性都更好。
1. 页面基础结构
先搭建基础的HTML结构,包含内容容器和底部触发加载的占位元素:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>无限滚动示例</title>
<style>
.content-container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.item {
padding: 15px;
margin-bottom: 10px;
border: 1px solid #eee;
border-radius: 4px;
}
.loading-trigger {
height: 50px;
line-height: 50px;
text-align: center;
color: #999;
}
</style>
</head>
<body>
<div class="content-container" id="contentContainer"></div>
<div class="loading-trigger" id="loadingTrigger">加载中...</div>
</body>
</html>2. 用Intersection_Observer实现加载逻辑
Intersection_Observer可以监听目标元素是否进入视口,不需要频繁计算滚动位置,性能更优:
// 模拟加载数据
function loadData() {
return new Promise((resolve) => {
setTimeout(() => {
const data = [];
const start = document.querySelectorAll('.item').length;
for (let i = start; i < start + 10; i++) {
data.push(`这是第${i + 1}条内容`);
}
resolve(data);
}, 1000);
});
}
// 渲染内容
function renderContent(data) {
const container = document.getElementById('contentContainer');
data.forEach(text => {
const div = document.createElement('div');
div.className = 'item';
div.textContent = text;
container.appendChild(div);
});
}
// 初始化Intersection_Observer
const observer = new IntersectionObserver(async (entries) => {
const trigger = entries[0];
// 触发元素进入视口且当前没有在加载中
if (trigger.isIntersecting && !window.isLoading) {
window.isLoading = true;
const triggerEl = document.getElementById('loadingTrigger');
triggerEl.textContent = '加载中...';
const newData = await loadData();
renderContent(newData);
triggerEl.textContent = '加载更多';
window.isLoading = false;
// 如果加载的内容不足一屏,再次触发加载
if (newData.length < 10) {
observer.unobserve(triggerEl);
triggerEl.textContent = '没有更多内容了';
}
}
}, {
root: null, // 相对于视口
rootMargin: '0px',
threshold: 0.1 // 触发元素10%进入视口就触发
});
// 监听底部触发元素
observer.observe(document.getElementById('loadingTrigger'));
// 初始加载一次内容
window.onload = async () => {
const initData = await loadData();
renderContent(initData);
};性能优化技巧
无限滚动如果处理不好,很容易出现页面卡顿、内存占用过高的问题,下面这些优化技巧可以有效提升性能。
1. 滚动事件节流(如果用scroll监听方案)
如果不用Intersection_Observer,用传统的scroll事件监听,一定要做节流处理,避免频繁触发回调:
let timer = null;
window.addEventListener('scroll', () => {
if (timer) return;
timer = setTimeout(() => {
// 滚动位置判断逻辑
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
const clientHeight = document.documentElement.clientHeight;
const scrollHeight = document.documentElement.scrollHeight;
if (scrollTop + clientHeight >= scrollHeight - 50) {
// 触发加载
}
timer = null;
}, 200);
});2. 图片懒加载
如果无限滚动的内容包含大量图片,一定要开启懒加载,等图片进入视口再加载,减少初始请求和带宽占用:
<!-- 用data-src存储真实图片地址,初始src放占位图 --> <img data-src="https://ipipp.com/example.jpg" src="placeholder.jpg" class="lazy-img" alt="内容图片">
配合Intersection_Observer监听图片进入视口后替换src:
const imgObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.remove('lazy-img');
imgObserver.unobserve(img);
}
});
});
document.querySelectorAll('.lazy-img').forEach(img => imgObserver.observe(img));3. DOM节点回收
如果用户滚动加载了上百条甚至上千条内容,过多的DOM节点会占用大量内存,可以设置阈值,当内容超过一定数量时,删除顶部已经离开视口的内容:
function recycleDOM() {
const items = document.querySelectorAll('.item');
const maxItems = 50; // 最多保留50条内容
if (items.length > maxItems) {
const container = document.getElementById('contentContainer');
// 删除前items.length - maxItems条内容
for (let i = 0; i < items.length - maxItems; i++) {
container.removeChild(items[i]);
}
}
}
// 每次加载新内容后调用回收函数4. 避免重复加载
加载过程中要加锁,防止用户快速滚动时多次触发加载请求,上面示例中的window.isLoading就是简单的加载锁,加载完成前不会再次触发加载逻辑。
兼容性说明
Intersection_Observer现代浏览器都支持,如果需要兼容IE等旧浏览器,可以 fallback 到传统的scroll事件监听方案,同时在代码中做好条件判断即可。
无限滚动HTMLJavaScript性能优化Intersection_Observer修改时间:2026-06-04 17:45:33