JavaScript scroll事件监听致scrollY波动、滚动条故障求助
问题排查与修复:scroll事件中scrollY波动、滚动条故障
核心原因分析
你遇到的scrollY值波动、滚动条故障,本质是scroll事件中修改元素高度导致页面总高度动态变化,触发了循环scroll事件:
- 你的
.square元素是position: relative,修改它的height会直接改变父容器.demo-wrapper的高度,进而影响整个页面的总高度 - 页面高度变化会强制浏览器重新计算滚动位置,再次触发scroll事件,形成恶性循环,最终导致scrollY值来回跳动、滚动条异常
另外还有几个次要问题放大了异常:
- 箭头函数中
this.scrollY的this不指向window,应该直接用window.scrollY - 代码中使用了未定义的
modulus函数,应该替换为Math.abs来计算绝对值 - scroll事件触发频率极高,没有做节流处理,加剧了性能问题和异常波动
修复方案
1. 脱离文档流,避免修改元素高度影响页面布局
将.square的定位改为fixed(或absolute,根据需求调整),这样修改高度不会影响页面总高度:
.square{ position: fixed; /* 替换relative为fixed */ width:20px; height:20px; left: 50%; /* 配合fixed实现水平居中 */ transform: translateX(-50%); background-color:#DC3F0A ; }
2. 修正scrollY获取方式与未定义函数
把箭头函数中的this.scrollY改为window.scrollY,并用Math.abs替代modulus:
window.addEventListener('scroll',(event)=>{ let scroll = window.scrollY; // 修正this指向问题 console.log(scroll) let div let rate for(let j=0;j<3;j++){ div = document.getElementById(`sq${j}`) let { top: t } = div.getBoundingClientRect(); const diff = Math.abs(scroll - t); // 替换modulus为Math.abs rate=(400-diff)/4 rate*=2 rate-=(rate)/4 if(diff <=400 && rate>20){ div.style.height = `${rate}px`; } else { div.style.height = "20px"; } } })
3. 添加节流函数,降低scroll事件触发频率
scroll事件每秒会触发数十次,添加节流可以减少不必要的计算,优化性能:
// 节流函数 function throttle(func, delay = 16) { let timer = null; return function(...args) { if (!timer) { timer = setTimeout(() => { func.apply(this, args); timer = null; }, delay); } }; } // 用节流包装scroll事件回调 window.addEventListener('scroll', throttle((event)=>{ // 原回调逻辑... }));
4. 优化初始元素创建逻辑
可以简化元素创建的代码,同时避免重复查询DOM:
const demoWrapper = document.querySelector(".demo-wrapper"); for(let i=0;i<3;i++){ const temp = document.createElement('div') temp.id=`sq${i}` temp.classList.add('square', 'new') temp.style.top=`${100 + (i*5)}px` demoWrapper.appendChild(temp) }
完整修正后的代码
JavaScript
// 节流函数 function throttle(func, delay = 16) { let timer = null; return function(...args) { if (!timer) { timer = setTimeout(() => { func.apply(this, args); timer = null; }, delay); } }; } // 创建元素 const demoWrapper = document.querySelector(".demo-wrapper"); for(let i=0;i<3;i++){ const temp = document.createElement('div') temp.id=`sq${i}` temp.classList.add('square', 'new') temp.style.top=`${100 + (i*5)}px` demoWrapper.appendChild(temp) } // 监听scroll事件(带节流) window.addEventListener('scroll', throttle((event)=>{ let scroll = window.scrollY; console.log(scroll) for(let j=0;j<3;j++){ const div = document.getElementById(`sq${j}`) const { top: t } = div.getBoundingClientRect(); const diff = Math.abs(scroll - t); let rate=(400-diff)/4 rate*=2 rate-=(rate)/4 if(diff <=400 && rate>20){ div.style.height = `${rate}px`; } else { div.style.height = "20px"; } } }));
CSS
.square{ position: fixed; width:20px; height:20px; left: 50%; transform: translateX(-50%); background-color:#DC3F0A ; } body{ overflow: scroll; background-color: #353433; } html{ height:200%; } .new:hover{ background-color:white; }
HTML(无需修改)
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> <title>hey man this is my page</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous"> <link href="style.css" rel="stylesheet" type="text/css" /> </head> <body> <div class="demo-wrapper "> </div> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN" crossorigin="anonymous"></script> <script src="https://replit.com/public/js/replit-badge.js" theme="blue" defer></script> <script src="script.js"></script> </body> </html>
内容的提问来源于stack exchange,提问作者shehzail abbas
相关产品推荐
相关产品推荐

