最近在作网页的时候有个需求,就是浏览器窗口改变的时候须要改一些页面元素大小,因而乎很天然的想到了window的resize事件,因而乎我是这么写的javascript
<!DOCTYPE html> <html> <head> <title>Throttle</title> </head> <body> <script type="text/javascript"> n=0; function resizehandler(){ console.log(new Date().getTime()); console.log(++n); } window.onresize=resizehandler; </script> </body> </html>
功能却是实现了,可是我拖拽的方式改变浏览器窗口大小的时候看了下控制台html
没错,简单的一个拖拽让个人resizeHandler()方法执行了52次,这彻底不是我想要的效果,实际上个人resizeHandler()方法的代码很复杂,甚至会使用ajax向服务器发送请求,要是简单的一次改变窗口大小就要调用52次这还了得java
其实个人本意只是窗口resize后页面作一些调整就能够,而window的resize事件并非在resize结束后才触发的,具体则么个频率我也不知道,但倒是在不停的调用,直到窗口大小再也不变化。其实相似的机制还有鼠标的mousemove,都是在短期内重复触发。ajax
在《JavaScript高级程序设计》中有专门应对此问题的函数节流浏览器
function throttle(method,context){ clearTimeout(method.tId); method.tId=setTimeout(function(){ method.call(context); },500); }
原理很简单,利用定时器,让函数执行延迟500毫秒,在500毫秒内若是有函数又被调用则删除上一次调用,此次调用500毫秒后执行,如此往复。这样我刚才的代码能够改成服务器
<script type="text/javascript"> n=0; function resizehandler(){ console.log(new Date().getTime()); console.log(++n); } function throttle(method,context){ clearTimeout(method.tId); method.tId=setTimeout(function(){ method.call(context); },500); } window.onresize=function(){ throttle(resizehandler,window); }; </script>
拖拽一下试试,果然只执行了一次闭包
网上还有一种函数节流方案,它是这么作的app
function throttle(method,delay){ var timer=null; return function(){ var context=this, args=arguments; clearTimeout(timer); timer=setTimeout(function(){ method.apply(context,args); },delay); } }
调用一下试试,同样的效果函数
<script type="text/javascript"> n=0; function resizehandler(){ console.log(new Date().getTime()); console.log(++n); } function throttle(method,delay){ var timer=null; return function(){ var context=this, args=arguments; clearTimeout(timer); timer=setTimeout(function(){ method.apply(context,args); },delay); } } window.onresize=throttle(resizehandler,500);//由于返回函数句柄,不用包装函数了 </script>
两种方法都是利用了setTimeout,不一样的是第二种方法加入的函数延迟执行时间,这个在第一种方案中很容易也具备此功能,加一个参数的事儿。this
但第一种方案把tId设为函数的一个变量保存,而第二种建立了一个闭包来存储。我的以为差距不大,很喜欢第一种,简单,高效。
有一天作了个相似的东西,就像百度首页输入自动提示同样的东西,我在text上绑定keyup事件,每次键盘弹起的时候自动提示,可是又不想提示那么频繁,因而我用了上面方法,可是悲剧了,只有中止输入等500毫秒才会提示,在输入过程当中根本就没有提示。看了一下代码,可不是嘛,只要是用户会盲打,在500毫秒内按一下键盘,提示函数就会不断被延迟,这样只有停下来的时候才会提示,这就没意义了。
能不能在函数节流的基础上间隔固定时间就执行一次?
在网上搜了一下咱们能够根据第二种写法(第一种为函数拓展多个变量感受有些很差)作些改动,添加一个参数做为到固定间隔必须执行
function throttle(method,delay,duration){ var timer=null, begin=new Date(); return function(){ var context=this, args=arguments, current=new Date();; clearTimeout(timer); if(current-begin>=duration){ method.apply(context,args); begin=current; }else{ timer=setTimeout(function(){ method.apply(context,args); },delay); } } }
这样每次咱们判断间隔了多久,要是超过设置时间则当即执行一次,以刚才例子试一试效果
window.onresize=throttle(resizehandler,100,200);
果然既没有频繁执行也没有就最后执行