删除超时函数并消除其 bug,您真正需要的一个事件是:scrollend。
在 scrollend
事件发生之前,没有可靠的方法来检测滚动是否已完成。这意味着事件会延迟触发,或者会在用户的手指仍按下时触发。由于无法可靠地知晓滚动何时真正结束,从而导致了错误并给用户带来糟糕的体验。
document.onscroll = event => { clearTimeout(window.scrollEndTimer) window.scrollEndTimer = setTimeout(callback, 100) }
此 setTimeout()
策略能做的最佳就是知道 100ms
的滚动是否已经停止。这更像是“滚动已暂停”事件,而不是“滚动已结束”事件。
在 scrollend
事件发生后,浏览器会为您执行所有这些高难度的评估。
document.onscrollend = event => {…}
这才是好事。在发出之前,恰到好处地计时且包含大量有意义的条件。
试试看!
活动详情
在以下情况下会触发 scrollend
事件:
- 浏览器不再以动画形式显示滚动或平移滚动。
- 用户的轻触操作已释放。
- 用户的指针已释放滚动滑块。
- 用户的按键已释放。
- 已完成滚动到 fragment。
- 滚动贴靠已完成。
- scrollTo()
已完成。
- 用户已滚动视觉视口。
在以下情况下不会触发 scrollend
事件:
- 用户的手势未导致任何滚动位置发生变化(未发生任何平移)。
- scrollTo()
未找到任何翻译。
该事件需要很长时间才引入 Web 平台的一个原因是,许多小细节需要规范详细信息。其中最复杂的一个环节就是清晰阐明视觉视口与文档的 scrollend
细节。以某个您放大的网页为例。在这种缩放状态下,您可以四处滚动,而无需滚动文档。请放心,即使是这种由用户驱动的视觉视口滚动互动在完成之后也会发出 scrollend
事件。
使用事件
与其他滚动事件一样,您可以通过多种方式注册监听器。
addEventListener("scrollend", (event) => {
// scroll ended
});
aScrollingElement.addEventListener("scrollend", (event) => {
// scroll ended
});
或者使用事件属性:
document.onscrollend = (event) => {
// scroll ended
};
aScrollingElement.onscrollend = (event) => {
// scroll ended
};
Polyfill 和渐进式增强
如果您现在打算使用这项新活动,以下是我们的最佳建议。您可以继续使用当前的滚动结束策略(如果有),并在开始时检查支持情况:
'onscrollend' in window
// true, if available
这将报告 true 或 false,具体取决于浏览器是否提供事件。通过这项检查,您可以为代码创建分支:
if ('onscrollend' in window) {
document.onscrollend = callback
}
else {
document.onscroll = event => {
clearTimeout(window.scrollEndTimer)
window.scrollEndTimer = setTimeout(callback, 100)
}
}
这是一个良好的开端,可以在 scrollend
事件可用时逐步增强该事件。您还可以尝试使用我开发的 polyfill (NPM),它可以为浏览器提供最佳性能:
import {scrollend} from "scrollyfills"
// then use scrollend as if it's existed this whole time
document.onscrollend = callback
polyfill 将逐步增强,以使用浏览器内置的 scrollend
事件(如果有)。如果不可用,脚本会监视指针事件并滚动,以尽可能准确地估算结束它的事件。
使用场景
最好避免在滚动时执行大量计算工作。这种做法可确保滚动操作可以释放尽可能多的内存和处理能力,从而确保体验流畅。使用 scrollend
事件是调用并执行这些繁琐工作的最佳时机,因为不会再发生滚动操作。
scrollend
事件可用于触发各种操作。一个常见的用例是将关联的界面元素与滚动停止的位置进行同步。例如:
- 将轮播界面滚动位置与圆点指示符同步。
- 将图库项与其元数据同步。
- 在用户滚动到新标签页后获取数据。
设想一个场景,例如用户滑开电子邮件。用户完成滑动后,您可以根据用户滚动到的位置执行操作。
您还可以使用此事件在程序化操作或用户滚动后进行同步,或者执行日志记录分析等操作。
下面就是一个很好的示例,其中箭头、点和焦点等多个元素需要根据滚动位置进行更新。观看我在 YouTube 上是如何打造此轮播界面的。此外,您还可以尝试现场演示。
感谢 Mehdi Kazemi 在这方面的工程工作,以及 Robert Flack 提供 API 和实现指导。