很多网站都会提供一个按钮:用于返回顶部。
这个按钮只会在滚动到距离顶部一定位置的时候才出现-(监听滚动事件,返回当前到顶部的距离)
function showTop() { var scrollTop = document.body.scrollTop || document.documentElement.scrollTop console.log(scrollTop) } window.onscroll = showTop
但是运行的时候存在一个问题:函数默认执行频率太高,按一次键盘的下方向键,函数就执行了9次!
实际应用上不需要高频执行反馈,这样会影响浏览器性能,如何优化成为问题。
第一种思路:防抖(debounce)
在第一次触发事件时,不立即执行函数,而给出一个期限值比如200ms
·如果在200ms内没有再次触发滚动事件,就执行函数
·如果在200ms内再次触发了滚动事件,当前计时取消,重新开始计时
效果:短时间能大量触发同一事件,只会执行一次函数。
实现:计时,实现的关键就是setTimeout函数,还需要一个变量来保存计时。考虑维护全局纯净,借助闭包实现
function debounce(fn,delay){ let timer = null return function(){ if(timer){ clearTimeout(timer) //进入该分支说明在计时过程中又触发了该事件,所以清除定时 timer = setTimeout(fn,delay) //重新开始计时,实际这句不用写,因为一定会执行计时 }else{ timer = setTimeout(fn,delay) //进入该分支说明当前没有在计时,开始计时) } } }
然后接着上面的代码 window.onscroll = debounce(showTop,1000)
此时,只有在停止滚动1秒之后,才会打印出滚动条的位置
定义:对于短时间内连续触发的事件,防抖的含义就是让某个时间期限内,事件处理函数只执行一次
function debounce(fn,delay){ let timer = null return function(){ if(timer){ clearTimeout(timer) } timer = setTimeout(()=>{ //保证可以将参数传入 fn.apply(this,arguments) }, delay); } } var ipt = document.getElementById('ipt'); ipt.addEventListener('keyup',debounce(function(e){ console.log(e.target) console.log(ipt.value) },1000))
监听一个input输入框keyup事件,解决event参数传参问题使用apply()
第二种思路:节流(throttle)
使用上面的防抖方案来处理问题的结果是:
·如果在限定时间段内,不断触发滚动事件(某个用户按住滚动条不断的拖来拖去),只要不停止触发,理论上就永远不会输出当前距离顶部的距离
怎么处理成为关键:设计一种类似控制阀门一样定期开放的函数,也就是让函数执行一次后,在某个时间段内失效,过了这段时间再重新激活(类似技能冷却时间)
function throttle(fn,delay){ let valid = true // 状态位标识当前函数是否处于工作状态 return function(){ if(!valid){ return false // 失效状态,不往下执行 } valid = false //先将标志位处于休息装填,在定时器计时完成之前不执行函数 setTimeout(() => { fn(); valid = true; // 进入定时器,执行函数再将阀门打开 },delay) } }
然后接着上面的代码 window.onscroll = throttle(showTop,1000)
上面代码运行结果是:如果一直拖着滚动条进行滚动,会以1s的时间间隔,持续输出当前位置和顶部距离
//更好理解版本,适配含event参数等使用fn.apply() function throttle(fn,delay){ let timer = null return function(){ if(timer){ return } timer = setTimeout(()=>{ fn.apply(this,arguments) //相比防抖要加这一句才会重复触发不然只执行一次 timer = null }, delay); } }
用一句话总结防抖和节流的区别:防抖是将多次执行变为最后一次执行,节流是将多次执行变为每隔一段时间执行
平时开发常遇到的场景:
1.搜索框input事件,例如要支持实时搜索就可以使用节流方案(间隔一段时间就必须查询相关的内容),或者实现输入间隔大于某个值,就当作用户输入完成,开始搜索,具体使用哪种方案需要看业务需求
2.页面resize事件,常用于需要做页面适配的时候。需要根据最终呈现的页面情况进行dom渲染(这种情形一般是使用防抖,因为只需要判断最后一次的变化情况)
https://segmentfault.com/a/1190000018428170