使用Intersection Observer实现无序列表循环连续滚动(JavaScript)
用Intersection Observer实现无序列表循环连续滚动
需求
- 向下滚动到最后一个
<li>元素时,自动将第一个<li>追加到<ul>末尾,实现无缝向下循环 - 向上滚动到顶部的
<li>元素时,自动将最后一个<li>前置到<ul>顶部,实现无缝向上循环
完整实现代码
HTML
<ul class="hour"> <li>项目 1</li> <li>项目 2</li> <li>项目 3</li> <li>项目 4</li> <li>项目 5</li> <li>项目 6</li> <li>项目 7</li> <li>项目 8</li> </ul>
CSS
.hour { height: 300px; overflow-y: auto; list-style: none; padding: 0; margin: 0; } .hour li { padding: 15px; border-bottom: 1px solid #eee; font-size: 16px; }
JavaScript
// 获取目标列表容器 const list = document.querySelector('.hour'); // 初始监听最后一个li(向下滚动触发) const lastTarget = list.querySelector('li:last-child'); // 初始监听第一个li(向上滚动触发) const firstTarget = list.querySelector('li:first-child'); // 向下滚动回调:将第一个元素移到末尾 const handleLastEntry = (entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const firstItem = list.firstElementChild; list.append(firstItem); // 重新监听新的最后一个元素 observerLast.unobserve(lastTarget); observerLast.observe(list.querySelector('li:last-child')); } }); }; // 向上滚动回调:将最后一个元素移到开头 const handleFirstEntry = (entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const lastItem = list.lastElementChild; list.prepend(lastItem); // 重新监听新的第一个元素 observerFirst.unobserve(firstTarget); observerFirst.observe(list.querySelector('li:first-child')); } }); }; // 配置项:调整触发时机的偏移量与可见度阈值 const options = { rootMargin: '-10px 0px', threshold: 1 }; // 创建两个Intersection Observer实例,分别处理上下滚动 const observerLast = new IntersectionObserver(handleLastEntry, options); const observerFirst = new IntersectionObserver(handleFirstEntry, options); // 启动监听 observerLast.observe(lastTarget); observerFirst.observe(firstTarget);
关键说明
- 双向监听覆盖场景:通过两个Observer分别监听首尾元素,同时支持上下滚动的循环逻辑
- 动态更新监听目标:每次移动元素后,必须重新绑定新的首尾元素作为监听目标,保证后续滚动能正确触发回调
- 触发时机控制:
threshold:1设置元素完全可见时触发回调,rootMargin调整触发的偏移距离,避免滚动时的频繁触发
内容的提问来源于stack exchange,提问作者B L Praveen
相关产品推荐
相关产品推荐

