删除超时函数并摆脱其 bug,您真正需要的事件是 scrollend。
在 scrollend 事件之前,没有可靠的方法来检测滚动是否完成。这意味着事件会延迟触发,或者在用户的手指仍按在屏幕上时触发。这种无法可靠地知道滚动何时实际结束的情况会导致 bug 和糟糕的用户体验。
document.onscroll = event => { clearTimeout(window.scrollEndTimer) window.scrollEndTimer = setTimeout(callback, 100) }
此 setTimeout() 策略最多只能知道滚动是否已停止 100ms。这使其更像是一个滚动已暂停事件,而不是滚动已结束事件。
在 scrollend 事件发生后,浏览器会为您完成所有这些复杂的评估。
document.onscrollend = event => {…}
这才是好东西。在发出之前,经过完美计时并包含大量有意义的条件。
试试看!
活动详情
在以下情况下,系统会触发 scrollend 事件:- 浏览器不再以动画方式显示或转换滚动。
- 用户已松开手指。
- 用户的指针已释放滚动条滑块。
- 用户的按键已释放。
- 滚动到片段已完成。
- 滚动吸附已完成。
- 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 和实现指南。