什么是防抖?
防抖就是对于频繁触发的事件添加一个延时同时设定一个最小触发间隔,如果触发间隔小于设定的间隔,则清除原来的定时,重新设定新的定时;如果触发间隔大于设定间隔,则保留原来的定时,并设置新的定时;防抖的结果就是频繁的触发转变为触发一次
为什么要进行防抖?
在频繁触发事件的场景,有些情况可能执行的逻辑比较复杂或者耗时,此时浏览器的处理跟不上触发,就会发生卡顿、假死或者事件堆积,这里防抖就可以一定程度上解决或者缓解这种故障。
常见的需要防抖的场景: 搜索框keyup、keydown等触发后台请求; 频繁改变窗口大小resize;鼠标移动mousemove事件;类似以上频繁触发并且处理逻辑较为耗时或者触发时需要请求后台接口;
如何做到防抖呢?
版本1
<html> <head> <meta charset="UTF-8"> <style> div { width: 500px; height: 300px; background: orange; } </style> </head> <body> <div id="div"></div> </body> <script> const div = document.getElementById(‘div‘); const debounce = (fn, wait) => { let timer; return function() { clearTimeout(timer); timer = setTimeout(() => { fn.apply(this, arguments); }, wait); } }; const fn = debounce((e) => { console.log(‘move‘, e); }, 1000) div.addEventListener(‘mousemove‘, fn); </script> </html>
执行结果: 在橙色区域移动鼠标,只在停止移动后才会触发一次事件;
如果我们想让事件立即触发一次呢?
版本2
<html> <head> <meta charset="UTF-8"> <style> div { width: 500px; height: 300px; background: orange; } </style> </head> <body> <div id="div"></div> </body> <script> const div = document.getElementById(‘div‘); const debounce = (fn, wait, immediate = false) => { let timer; return function() { if(timer) clearTimeout(timer); if(immediate) { let trigger = !timer; timer = setTimeout(() => { timer = null; }, wait); if(trigger) { fn.apply(this, arguments); } return; } timer = setTimeout(() => { fn.apply(this, arguments); }, wait); return; } }; const fn = debounce((e) => { console.log(‘move‘, e); }, 1000, true) div.addEventListener(‘mousemove‘, fn); </script> </html>
执行结果是: 立即触发一次,然后停止触发1s后才能再次触发
如果这个防抖时间比较长,比如是5s,我希望可以取消这种“挂起”状态
<html> <head> <meta charset="UTF-8"> <style> div { width: 500px; height: 300px; background: orange; } </style> </head> <body> <div id="div"></div> <button id="btn">取消debounce</button> </body> <script> const div = document.getElementById(‘div‘); const btn = document.getElementById(‘btn‘); const debounce = (fn, wait, immediate = false) => { let timer; const debounced =function(){ if(timer) clearTimeout(timer); if(immediate) { let trigger = !timer; timer = setTimeout(() => { timer = null; }, wait); if(trigger) { fn.apply(this, arguments); } return; } timer = setTimeout(() => { fn.apply(this, arguments); }, wait); return; } debounced.cancel = () => { clearTimeout(timer); timer = null; } return debounced; }; const fn = debounce((e) => { console.log(‘move‘, e); }, 2000, true) div.addEventListener(‘mousemove‘, fn); btn.addEventListener(‘click‘, fn.cancel) </script> </html>
点击一下按钮就可以啦。