You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Firefox中释放滚动时scroll-snap的scrollend事件触发两次的问题

解决Firefox中scroll-snap场景下scrollend事件触发两次的问题

在使用scroll-snap实现滚动对齐的容器中,Firefox会触发两次scrollend事件:第一次在用户停止交互(松开滚轮/手指)时触发,第二次在滚动对齐动画完成时触发。两次事件间隔极短且事件对象无差异,以下是两种可行的区分方法,仅处理动画完成的第二次事件。

原问题代码复现

.viewport {
  width:100px;
  height:100px;
  white-space:nowrap;
  overflow:auto;
  display:inline-block;
  scroll-snap-type: x mandatory;
}

.viewport > div {
  width:100px;
  height:100px;
  display:inline-block;
  scroll-snap-align: center;
}

.red{background:red;}
.green{background:green;}
.blue{background:blue;}
<div class="viewport">
   <div class="red">&raquo;</div>
   <div class="green">&raquo;</div>
   <div class="blue">&raquo;</div>
</div>

方法一:防抖函数过滤重复事件

利用两次事件间隔极短的特性,通过防抖函数只保留最后一次事件的回调执行,精准捕获动画完成的触发时机。

const viewport = document.querySelector('.viewport');

// 防抖函数,延迟50ms执行回调,可根据实际测试调整时长
const debounce = (func, delay = 50) => {
  let timeoutId;
  return (...args) => {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => func.apply(this, args), delay);
  };
};

// 业务逻辑处理函数
const handleFinalScrollEnd = (event) => {
  console.log('滚动对齐完成:', event);
  // 此处编写你的业务代码
};

viewport.addEventListener('scrollend', debounce(handleFinalScrollEnd));

原理说明

第一次scrollend触发后,防抖函数会启动定时器,但很快第二次事件会触发并清除之前的定时器,只有当延迟时间内无新事件触发时,才会执行最终的回调逻辑,确保只处理动画完成的那次事件。

方法二:检查滚动位置稳定性

通过requestAnimationFrame循环检测滚动位置是否稳定,判断动画是否完成,无需依赖固定延迟时间,适配性更强。

const viewport = document.querySelector('.viewport');
let lastScrollLeft = 0;
let isCheckingStability = false;

const handleScrollEnd = (event) => {
  if (isCheckingStability) return;
  isCheckingStability = true;

  const checkScrollStable = () => {
    const currentScrollLeft = viewport.scrollLeft;
    if (currentScrollLeft === lastScrollLeft) {
      // 滚动位置连续两帧不变,说明动画完成
      console.log('滚动对齐完成:', event);
      isCheckingStability = false;
    } else {
      lastScrollLeft = currentScrollLeft;
      requestAnimationFrame(checkScrollStable);
    }
  };

  lastScrollLeft = viewport.scrollLeft;
  requestAnimationFrame(checkScrollStable);
};

viewport.addEventListener('scrollend', handleScrollEnd);

原理说明

每次scrollend触发时,启动循环检测滚动位置:如果连续两帧的滚动位置完全一致,说明滚动对齐动画已经结束,此时执行业务逻辑;否则继续检测,直到位置稳定。

内容的提问来源于stack exchange,提问作者Grim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 06:11:09