关于scroll等类似高频率事件的最优实现攻略。
你会怎么实现一个监听HTML元素滚动到底部这个看起来很简单的函数方法?
1. 背景和目标
前端在监听scroll这类高频率触发事件时,常常需要一个监听函数来实现监听和回调处理。传统写法上利用setInterval或setTimeout来实现。
为了减小CPU开支,往往需要节流函数,但是,interval的指定依旧是个难题。interval较大,会处理不及时;较小,占用内存资源。
为了实践和解决问题,打算实现一个监听HTML元素滚动到底部的函数:
- 监听指定HTML元素的
scroll事件,并正确判断是否到底部- 正确确定确定回调间隔
- 正确使用节流函数
- 组件封装
2. window.requestAnimationFrame()
前文说到,如果利用
setTimeout或者setInterval,回调间隔interval很难确定。最理想的情况就是:回调间隔等于显示屏(浏览器)刷新频率。
浏览器刷新频率一般会略低于显示屏刷新频率,为16.7次/ms。具体说,就是:scroll事件每次触发时候的时间间隔。通过代码来看一下:
1 2 3 4 |
var app = document.getElementById('app')
app.addEventListener('scroll' , function() {
console.log((new Date()).getTime())
})
|
可以看到,有时候间隔是10ms,有时候是30ms,如果我们自己来设定interval,应该取最小值。然而,不同浏览器和不同电脑的刷新频率不确定。如果设置过小,还会造成刷新频率低的显示屏的CPU损耗。
所以,使用window.requestAnimationFrame()来让浏览器根据刷新频率自动设置interval。我们只需要关注回调函数即可。
当然,这个函数本身还实现了很多优化,可以点我看一下。
3. 节流函数
由于window.requestAnimationFrame()的特效,所以它可以在同一帧中被重复绘制。这时候,就需要节流函数,保证requestAnimationFrame的回调队列中只有一个函数在执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// 节流函数 : 减少浏览器内存消耗
function throttle(ele , callback) {
var isRunning = false
return function() {
if (isRunning) return
isRunning = true
// requestAnimationFrame:回调间隔 = 浏览器重绘频率
window.requestAnimationFrame(function(timestamp) {
if(ele.scrollTop + ele.clientHeight >= ele.scrollHeight) { // 检测是否滚动到元素底部
callback()
}
isRunning = false
})
}
}
|
4. 代码封装
函数封装详见script.js,调用样例详见index.html
基于上面,我们封装script.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
// 节流函数 : 减少浏览器内存消耗
function throttle(ele , callback) {
var isRunning = false
return function() {
if (isRunning) return
isRunning = true
// requestAnimationFrame:回调间隔 = 浏览器重绘频率
window.requestAnimationFrame(function(timestamp) {
if(ele.scrollTop + ele.clientHeight >= ele.scrollHeight) { // 检测是否滚动到元素底部
callback()
}
isRunning = false
})
}
}
/**
* 监听HTML元素是否滚动到底部 : 兼容ES5
* @param {object} ele HTML元素
* @param {function} callback 滚动到底部后的回调函数
*/
function listenScrollToBottom(ele , callback) {
if(ele === null || ele === undefined) { // 节点不存在:抛出错误
throw new Error('Undefined COM')
return
}
ele.addEventListener("scroll" , throttle(ele , callback) , false) // 监听 scroll 事件
}
|
将需要监听的HTML元素和回调函数传入,即可在HTML元素滚动到底部时,触发相应的操作。例如:瀑布流、缓冲加载等。下面是控制台输出一段文字。
1 2 3 4 5 6 7 8 9 10 11 |
<body>
<div id="app">
<div class="inner"></div>
</div>
<script>
var app = document.getElementById('app')
listenScrollToBottom(app , function() { // 回调函数
console.log("Scroll to bottom")
})
</script>
</body>
|
本文作者 : 董沅鑫
原文链接 : https://godbmw.com/passages/2018-06-06-best-event-listener/