scroll事件在文档或文档元素滚动时触发,主要出现在用户拖动滚动条。
window.addEventListener('scroll', callback);
该事件会连续地大量触发,所以它的监听函数之中不应该有非常耗费计算的操作。
推荐的做法是使用requestAnimationFrame
或setTimeout
控制该事件的触发频率,然后可以结合customEvent
抛出一个新事件。
var throttle = function (type, name, obj) {
var obj = obj || window;
var running = false;
var func = function () {
if (running) { return; }
running = true;
requestAnimationFrame(function() {
obj.dispatchEvent(new CustomEvent(name));
running = false;
});
};
obj.addEventListener(type, func);
};
// 将 scroll 事件重定义为 optimizedScroll 事件
throttle('scroll', 'optimizedScroll');
})();
window.addEventListener('optimizedScroll', function() {
console.log('Resource conscious scroll callback!');
});
上面代码中,throttle
函数用于控制事件触发频率,requestAnimationFrame
方法保证每次页面重绘(每秒60次),只会触发一次scroll事件的监听函数。也就是说,上面方法将scroll事件的触发频率,限制在每秒60次。具体来说,就是scroll
事件只要频率低于每秒60次,就会触发optimizedScroll
事件,从而执行optimizedScroll
事件的监听函数。
setTimeout
方法,可以放置更大的时间间隔。
(function() {
window.addEventListener('scroll', scrollThrottler, false);
var scrollTimeout;
function scrollThrottler() {
if (!scrollTimeout) {
scrollTimeout = setTimeout(function () {
scrollTimeout = null;
actualScrollHandler();
}, 66);
}
}
function actualScrollHandler() {
// ...
}
}());
上面代码中,每次scroll事件都会执行scrollThrottler
函数。该函数里面有一个定时器setTimeout
,每66毫秒触发一次(每秒15次)真正执行的任务actualScrollHandler
。
function throttle(fn, wait) {
var time = Date.now();
return function() {
if ((time + wait - Date.now()) < 0) {
fn();
time = Date.now();
}
}
}
window.addEventListener('scroll', throttle(callback, 1000));
上面的代码将scroll事件的触发频率,限制在一秒一次。
lodash
函数库提供了现成的throttle
函数,可以直接使用。
window.addEventListener('scroll', _.throttle(callback, 1000));
知识拓展
debounce
与throttle
的却别
throttle
是“节流”,确保一段时间内只执行一次,而debounce
是“防抖”,要连续操作结束后再执行。以网页滚动为例,debounce
要等到用户停止滚动后才执行,throttle
则是如果用户一直在滚动网页,那么在滚动过程中还是会执行。