“如何稀释scroll事件”引出的问题

背景:我在segmentfault提了个问题如何稀释onscroll事件,问题如下:

面试时问到这个问题,是这样的:
    面试官问一个关于滚动到某个位置的时候出现一个顶部的导航栏,答完之后,她接着问一滚动onscroll就会执行很多很多次,如何稀释它?为了确定她说的是“稀释”,我让她重复了遍,我给出的解决方法是,用一个变量,在事件处理的时候让它自增,判断达到一定大小就执行一次实际的事件:

var i = 0;    // 累积变量
window.onscroll = function(){
    i++;
    if(i%500==0){
        // 执行实际的事件
    }
}
  • 她并不满意,问最后如何释放这个变量?
  • ……
  • 接着她说:“我要的是稀释onscroll的执行次数,而不是这个(我所指的实际的事件)的执行次数。”
  • 我很是不解,鼠标一滚动就触发这个事件,如何能减少它的执行次数,如何稀释它?难道动态绑定/解绑事件,如何操作?

    网上都没有找到相关类型的问题,这个问题算不算变态级的面试题?如果不是,请给出解决方案,先谢谢了。

第一个回答认为这是函数节流,顿时恍然大悟,纳为答案,这是我和该名词的初次接触,本以为就这样结束了,没想到接下来又吸引了一大波程序员的眼球。

基本地,指出使用 throttle 和 debounce 两种方式:

throttle(译:节流阀):就是函数节流的意思,控制函数调用的频度,固定时间间隔执行,即连续的调用中,不管频度如何,只间隔固定时间(或大于该时间,这时频度较低)执行一次,不是问题中的“稀释”。

debounce(译:防反跳?):就是去抖的意思,空闲控制,在一定空闲时间间隔内的调用不予实现,一个简单的实现如下:

 1 var timer = null;
 2 document.addEventListener(‘mousemove‘, function () {
 3         if (timer) {
 4             clearTimeout(timer)
 5         }
 6         timer = setTimeout(function(){
 7             console.log("mousemove");
 8         }, 100);
 9     }
10 );

主要应对高度频发的调用,100的意思不是100ms执行一次,而是当调用间隔时间不超过100ms,即鼠标移动速度过快的话,console.log()会一直不被执行,除非移动间隔时间大于100ms,用网友bumfod的话,「函数节流让一个函数只有在你不断触发后停下来歇会才开始执行,中间你操作得太快它直接无视你。」,不是问题中的“稀释”。

综上,诚然 throttle 和 debounce 都能很好地解决性能问题,二者稀释的是业务逻辑的执行次数,但都不是问题所要求的,这时我就觉得这个问题有点牵强了,因为不管怎样,不管有没有显性地定义 scroll 事件,浏览器都会触发 scroll 事件的,差别在于有没有 callback,有没有要执行的东西而已。

如果非要“减少scroll 的执行次数”,这里有一位和我不谋而合的网友代码,通过setTimeout,执行一次再延时重新绑定事件监听器,这种方法稀释的是回调的执行次数

 1 var cb = {
 2   onscroll:function() {
 3     console.log("scrolling");
 4     window.removeEventListener("scroll", cb.onscroll, false);   // 这里移除事件监听器
 5     setTimeout(function() {
 6         console.log("DONE");
 7         window.addEventListener("scroll", cb.onscroll, false);
 8     }, 200);    // 200ms后重新绑定事件监听器
 9   }
10 };
11 window.addEventListener("scroll", cb.onscroll, false);

还有同学引出了阻塞渲染、影响页面UI响应等的问题,

其他:

框架辅助 _debounce(underscore.js 里的 debounce 函数)

 1 /**
 2  * [debounce description]
 3  * @param  {[type]} func      [回调函数]
 4  * @param  {[type]} wait      [等待时长]
 5  * @param  {[type]} immediate [是否立即执行]
 6  * @return {[type]}           [description]
 7  */
 8 _.debounce = function(func, wait, immediate) {
 9     var timeout, args, context, timestamp, result;
10
11     var later = function() {
12         var last = _.now() - timestamp;
13
14         //小于wait时间,继续延迟wait-last执行later,知道last >= wait才执行func
15         if (last < wait && last > 0) {
16             timeout = setTimeout(later, wait - last);
17         } else {
18             timeout = null;
19             if (!immediate) {
20                 result = func.apply(context, args);
21
22                 if (!timeout) context = args = null;
23             }
24         }
25     };
26
27     return function() {
28         context = this;
29         args = arguments;
30         timestamp = _.now();
31         //是否立即执行
32         var callNow = immediate && !timeout;
33
34         if (!timeout) timeout = setTimeout(later, wait);
35
36         if (callNow) {
37             result = func.apply(context, args);
38             context = args = null;
39         }
40
41         return result;
42     };
43 };

underscore.js 的 debounce

typeahead 的 throttle 实现源码:

 1 throttle: function(func, wait) {
 2     var context, args, timeout, result, previous, later;
 3     previous = 0;
 4     later = function() {
 5         previous = new Date();
 6         timeout = null;
 7         result = func.apply(context, args);
 8     };
 9     return function() {
10         var now = new Date(),
11             remaining = wait - (now - previous);
12         context = this;
13         args = arguments;
14         if (remaining <= 0) {   //如果大于间隔时间(wait)
15             clearTimeout(timeout);
16             timeout = null;
17             previous = now;
18             result = func.apply(context, args);
19         } else if (!timeout) {  //小于,延时调用later
20             timeout = setTimeout(later, remaining);
21         }
22         return result;
23     };
24 },

typeahead.js 的 throttle

如有纰漏,恳请指出,共同进步,谢谢^_^

时间: 2024-08-25 16:24:31

“如何稀释scroll事件”引出的问题的相关文章

“如何稀释scroll事件”的思考

看了下园友的一帖子:http://www.cnblogs.com/xzhang/p/4145697.html#commentform 本来以为是很简单的问题,但仔细想想还挺有意思的.简单的说就是增加事件触发的间隔时间. 比如在浏览器状态了事件是1毫秒调用一次,转换成100毫秒调用一次. 看了下原贴的两方法,觉得可以乐观锁的方法再写个,结果对比后发觉和typeahead差不多.贴下代码和测试页面.看那位能指点下写的更好.:) var _lazyRun = function (func, wait)

“如何稀释scroll事件”的思考(不小心写了个异步do...while)

看了下园友的一帖子:http://www.cnblogs.com/xzhang/p/4145697.html#commentform 本来以为是很简单的问题,但仔细想想还挺有意思的.简单的说就是增加事件触发的间隔时间. 比如在浏览器的事件是1毫秒调用一次,转换成100毫秒调用一次. 看了下原贴的两方法,觉得可以乐观锁的方法再写个,虽然最后对比结果和typeahead差不多.但过程还是挺有意思的,分享下思路 首先,浏览器事件间隔是没法改变的.所以我们只能改变回调函数的执行间隔. 乐观锁机制的流程是

javascript中的scroll事件

window.addEventListener('scroll',function(){ if(document.compatMode == "CSS1Compat") { alert(document.documentElement.scrollTop); } else { alert(document.body.scrollTop); } },false); 虽然scroll事件是在window对象上发生的,但它事件表示的则是页面中相应元素的变化.在混杂模式中,可以通过<bo

scroll事件实现监控滚动条并分页显示示例(zepto.js)

scroll事件实现监控滚动条并分页显示示例(zepto.js  ) 需求:在APP落地页上的底部位置显示此前其他用户的购买记录,要求此div盒子只显示3条半,但一页有10条,div内的滑动条滑到一页底部自动加载下一页并发加载埋点. 实现:首先理解三个概念,分别是contentH,viewH,scrollTop. contentH:即所要滑动的元素内容的高度,包括可见部分以及滚动条下面的不可见部分. ViewH:即我们看到的这个DIV的高度,不包括可见部分也不包括滚动条下面的不可见部分. scr

scroll事件在IE,chrome,FF中的行为表现

周末想做一个滚动加载图片的效果.在IE,chrome,FF中验证时,忽然发现了一个有趣的问题. 问题是这样的: 1.     我发现鼠标滚动一次,scroll事件的处理函数执行的次数不一样. 2.     滚动的距离也不一样. 3.     点击滚动条滚动时,执行次数,滚动距离也不一样. 经过验证:        执行次数: 在IE5,6,7,8下,滚动一次,函数执行4次: 在IE9,10以上版本执行1次: 在chrome下,只执行一次.(chrome版本号:34.0.1847.116 m) 在

扩展jquery scroll事件,支持 scroll start 和 scroll stop

参考地址: http://www.ghugo.com/special-scroll-events-for-jquery/ javascript里有一个事件是滚动事件,只要拖动滚动条,就会触发事件. 用jquery的话,这个事件scroll 可以查看jquery api :http://api.jquery.com/scroll/ 但scroll 事件有一个缺陷,就是只能判断滚动条滚动,而不能监控滚动条停止滚动时的事件. 现用jquery扩展一下scroll 事件,新增 不多说,直接上代码实在点.

jQuery事件:scroll事件

scroll事件:滚动元素时或者是使用scroll()方法会触发scroll事件 scroll()方法 触发选中元素的scroll事件 $(selector).scroll()//无参数 规定scroll事件被触发后的函数 $(selector).scroll( function ) function:js Function对象 详情

jQuery scroll事件实现监控滚动条分页示例(转)

这篇文章主要介绍了jQuery scroll事件实现监控滚动条分页简单示例,使用ajax加载,同时介绍了(document).height()与$(window).height()的区别,需要的朋友可以参考下. scroll事件适用于window对象,但也可滚动iframe框架与CSS overflow属性设置为scroll的元素. 1 $(document).ready(function () { //本人习惯这样写了 2 $(window).scroll(function () { 3 //

jquery scroll事件

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>scroll Demo</title