基于 Opera 12 实现的系统级 scroll 事件防抖

2017-08-05 07:58:06 +08:00
 autoxbc

前情提要: 在 为何 iOS 的 scroll 阻塞 dom 机制没被大规模借鉴? 中提到,iOS 的 UIWebView 有个特性,页面滚动时会阻塞 js 的 scroll 事件触发和 css 的页面重绘,大大提高了滚动性能。本质上说,这是在前端开发者之外,在浏览器上实现的 scroll 防抖优化,哪怕程序员并不需要(破坏一致性)。

作为用户我是需要的,因为有些人写的代码实在不靠谱。我要在桌面浏览器上实现同样的功能,即对那些包含糟糕滚动性能的页面,统一加上 scroll 防抖处理:对一连串 scroll 动作,只在最后触发一个 scroll 事件,哪怕开发者没做任何防抖和节流。

在 Opera 12 浏览器中,存在一个全局 Opera 对象,提供一些堪称魔法的函数,让用户对网页事件流做细致的调整,举个例子:
有些网页会监听用户的 copy 动作,显示一个难看的提示框,我们这么处理

opera.addEventListener('BeforeEventListener.copy',function(e){
    e.preventDefault();
});

哪怕不知道回调函数,对 copy 事件的监听也被去除了,这个方法是本文的核心。

为了观察 scroll 是否完全停止,用一个全局变量保存 scroll 事件的时间戳。

window.lastScroll = new Date();

为了即使自己的其他代码禁用 scroll 事件,也可以正确的记录时间戳,同样使用 Opera 对象

opera.addEventListener('AfterEvent.scroll', function (e){
    window.lastScroll = e.timeStamp ;
});

即使 scroll 事件不触发,Opera 也可以捕获 AfterEvent.scroll 事件,时间戳是一样的。

下面对 scroll 事件做防抖,不要介意我的渣代码

opera.addEventListener('BeforeEventListener.scroll',function(e){
    // 阻止事件触发,禁用原有的回调
    e.preventDefault();

    var callee = arguments.callee ;
    // timer 为事件墙,只让第一个 scroll 事件通过,其他废弃
    if( callee.timer )
        return;

    // 首次滚动立即延迟,观察有无后续滚动
    // 同时记录时间戳
    callee.timer = setTimeout( function(){
        // 高于阈值,滚动完全结束,进入回调组装流程
        if( new Date() - window.lastScroll > 800 )
        {
            // 清理事件墙
            callee.timer = null ;
            // 提取原来的回调函数 e.listener
            // 提取原来的 scroll 事件 e.event
            // 组合并触发回调
            e.listener(e.event);
        } else {
            // 低于阈值,滚动未结束,继续延迟回调
            // 同时更新时间戳
            callee.timer = setTimeout( arguments.callee , 400 );
        }
    } , 400 );
});

这段代码的逻辑是,从开发者对 scroll 的监听过程捕获 BeforeEventListener.scroll 事件,提取其中的 scroll 事件( e.event ),回调函数( e.listener ),按照一定的逻辑重新组装到一起,或者立即触发,或者延迟执行。

在延迟的过程中,对一串 scroll 事件,只在滚动停止后触发一次。

至此,借助浏览器提供的专有对象,实现了 scroll 事件的系统级防抖处理,滚动体验被大大提升。在我有限的认知里,没有其他浏览器有这么强的内置功能,可以实现类似的效果。如果 Chrome 或者 Firefox 能够做到,请留言告诉我。

2756 次点击
所在节点    浏览器
0 条回复

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/380571

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX