JS的window.addEventListener仅单页生效及offset未定义报错问题
解决元素可见性检测函数仅单页生效且报错的问题
问题根源分析
你碰到的TypeError: $elem.offset(...) is undefined报错,本质原因是其他页面不存在你代码中检测的部分元素(比如#lfdesign、#books这类选择器对应的DOM节点)。当jQuery尝试获取不存在元素的offset()属性时,会直接返回undefined,导致代码执行中断,整个滚动事件的处理逻辑也就失效了。
修复方案
1. 先完善isOnScreen函数的空元素判断
原函数里的空元素判断只是直接return,但没有返回明确的布尔值,后续代码还是会尝试执行offset()操作。我们修改成直接返回false,并提前获取jQuery对象做判断:
function isOnScreen(elem) { var $elem = jQuery(elem); // 元素不存在直接返回false,避免后续报错 if ($elem.length === 0) { return false; } var $window = jQuery(window) var viewport_top = $window.scrollTop() var viewport_height = $window.height() var viewport_bottom = viewport_top + viewport_height var top = $elem.offset().top var height = $elem.height() var bottom = top + height return (bottom > viewport_top) && (top < viewport_bottom) }
2. 优化滚动事件逻辑(可选但推荐)
滚动事件触发频率极高,建议加上防抖函数减少性能消耗,同时给已添加动画的元素做标记,避免重复操作:
// 防抖函数:控制事件触发频率 function debounce(func, wait) { let timeout; return function() { const context = this; const args = arguments; clearTimeout(timeout); timeout = setTimeout(() => func.apply(context, args), wait); }; } // 用防抖包装滚动事件处理函数 window.addEventListener('scroll', debounce(function (e) { // 统一管理所有需要动画的元素配置 const animateConfigs = [ { target: '#lfdesign', animateClass: 'animated slower bounceInRight' }, { target: '#books', animateClass: 'animated slower bounceInLeft' }, { target: '.srp-color', animateClass: 'animated slower bounceInRight' }, { target: '.nescol-color', animateClass: 'animated slower bounceInLeft' }, { target: '.worldskills-color', animateClass: 'animated slower bounceInRight' }, { target: '.silvernote-color', animateClass: 'animated slower bounceInLeft' } ]; animateConfigs.forEach(config => { const $container = jQuery(`${config.target} .flexcontainer`); // 容器不存在或已添加动画类,直接跳过 if ($container.length === 0 || $container.hasClass('animated')) return; // 元素进入视口时添加动画类 if (isOnScreen(config.target)) { $container.addClass(config.animateClass); } }); }, 100)); // 100ms防抖延迟,可根据需求调整
3. 进阶方案:使用Intersection Observer API替代滚动监听
现代浏览器支持的Intersection Observer API比滚动监听更高效,它能自动检测元素是否进入视口,无需手动计算位置,也不用处理防抖:
// 初始化观察器 const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const $container = jQuery(entry.target).find('.flexcontainer'); // 根据目标元素的标识添加对应动画类 const target = entry.target; if (target.id === 'lfdesign' || target.classList.contains('srp-color') || target.classList.contains('worldskills-color')) { $container.addClass('animated slower bounceInRight'); } else if (target.id === 'books' || target.classList.contains('nescol-color') || target.classList.contains('silvernote-color')) { $container.addClass('animated slower bounceInLeft'); } // 元素进入视口后停止观察,避免重复触发 observer.unobserve(entry.target); } }); }, { threshold: 0.1 }); // 元素10%进入视口时触发动画 // 批量观察目标元素,不存在的元素会自动跳过 ['#lfdesign', '#books', '.srp-color', '.nescol-color', '.worldskills-color', '.silvernote-color'].forEach(selector => { const elem = document.querySelector(selector); if (elem) observer.observe(elem); });
内容的提问来源于stack exchange,提问作者Louise Findlay
相关产品推荐
相关产品推荐

