如何解决JavaScript中滚动类检测延迟与Android兼容性问题?
滚动方向检测在Android浏览器异常:延迟/失效问题
问题详情
滚动时scrollup和scrolldown类的检测存在延迟,该功能在iOS设备上运行正常,但在Android设备上仅在Mozilla Firefox浏览器中有卡顿地运行,其他浏览器完全无法正常工作。已尝试移除AOS滚动行为、检查SCSS等操作,问题仍未解决。
原代码如下:
/** * @file * Global utilities. * */ (function (Drupal) { 'use strict'; Drupal.behaviors.bootstrap_barrio = { attach: function (context, settings) { var position = window.scrollY; window.addEventListener('scroll', function() { if (window.scrollY > 50) { document.querySelector('body').classList.add("scrolled"); } else { document.querySelector('body').classList.remove("scrolled"); } var scroll = window.scrollY; if (scroll > position) { document.querySelector('body').classList.add("scrolldown"); document.querySelector('body').classList.remove("scrollup"); } else { document.querySelector('body').classList.add("scrollup"); document.querySelector('body').classList.remove("scrolldown"); } position = scroll; }); document.addEventListener('click', function (event) { // If the clicked element doesn't have the right selector, bail if (!event.target.matches('.dropdown-item a.dropdown-toggle')) return; // Don't follow the link event.preventDefault(); toggle(event.target.next('ul')); event.stopPropagation(); }, false); // Toggle element visibility var toggle = function (elem) { // If the element is visible, hide it if (window.getComputedStyle(elem).display === 'block') { hide(elem); return; } // Otherwise, show it show(elem); }; var show = function (elem) { elem.style.display = 'block'; }; var hide = function (elem) { elem.style.display = 'none'; }; } }; } )(Drupal);
问题分析
原代码存在几个核心性能瓶颈:
- scroll事件高频触发:Android浏览器对高频事件的主线程分配优先级低,导致回调延迟执行
- 重复DOM查询:每次scroll回调都重复调用
document.querySelector('body'),增加不必要的性能开销 - 无节流控制:未限制回调执行频率,导致主线程被频繁占用,引发卡顿
- DOM方法误用:
event.target.next('ul')是jQuery语法,原生环境下会失效
解决方案
优化后的代码如下:
(function (Drupal) { 'use strict'; Drupal.behaviors.bootstrap_barrio = { attach: function (context, settings) { // 缓存DOM元素,避免重复查询 const body = document.querySelector('body'); if (!body) return; let lastScrollY = window.scrollY; let ticking = false; // 标记是否已有等待执行的回调 // 处理滚动逻辑的核心函数 const updateScrollState = () => { const currentScrollY = window.scrollY; // 更新scrolled类 if (currentScrollY > 50) { body.classList.add('scrolled'); } else { body.classList.remove('scrolled'); } // 更新滚动方向类,忽略微小滚动波动 const scrollDelta = currentScrollY - lastScrollY; if (Math.abs(scrollDelta) > 2) { // 阈值过滤滚动抖动 if (scrollDelta > 0) { body.classList.add('scrolldown'); body.classList.remove('scrollup'); } else { body.classList.add('scrollup'); body.classList.remove('scrolldown'); } } lastScrollY = currentScrollY; ticking = false; }; // 使用requestAnimationFrame节流scroll事件 window.addEventListener('scroll', () => { if (!ticking) { window.requestAnimationFrame(updateScrollState); ticking = true; } }, { passive: true }); // passive优化滚动性能,避免阻塞默认行为 // 下拉菜单逻辑修复 document.addEventListener('click', function (event) { if (!event.target.matches('.dropdown-item a.dropdown-toggle')) return; event.preventDefault(); toggle(event.target.nextElementSibling); // 替换为原生DOM方法 event.stopPropagation(); }, false); const toggle = function (elem) { if (!elem) return; if (window.getComputedStyle(elem).display === 'block') { hide(elem); return; } show(elem); }; const show = function (elem) { elem.style.display = 'block'; }; const hide = function (elem) { elem.style.display = 'none'; }; } }; })(Drupal);
优化说明
- DOM缓存:提前获取
body元素,避免每次scroll回调重复查询DOM - 事件节流:通过
requestAnimationFrame确保回调只在浏览器重绘前执行一次,降低主线程压力 - 滚动阈值:忽略小于2px的滚动波动,避免快速滚动时的方向误判
- passive监听:标记scroll事件为passive,允许浏览器优化滚动性能,避免卡顿
- 原生DOM方法修复:将jQuery的
next()替换为原生nextElementSibling,确保在非jQuery环境下正常运行
内容的提问来源于stack exchange,提问作者osmnfth
相关产品推荐
相关产品推荐

