这篇文章中主要介绍一下函数节流,而后给了一个图片懒加载的例子,说图片懒加载的时候顺带提了下怎么使用JS获取页面的宽高,卷上去的长度等。参考来源主要是《JavaScript高级程序设计》。html
浏览器的DOM操做比起非DOM交互须要更多的内存和cpu时间,连续过多的DOM操做可能会致使浏览器挂起甚至崩溃。好比使用onresize,onscroll这些可能会被连续触发的事件的时候,若是事件处理程序中进行了过多地DOM操做,可能就会使得浏览器崩溃。而为了绕开这个问题,可能就须要使用到函数节流。
好比:浏览器
function resizeDiv(){ //在窗口尺寸改变的时候,调整div的高度 var div = document.getElementById("myDiv"); console.log(div.offsetWidth); div.style.height = div.offsetWidth + "px"; } window.onresize = function(){ resizeDiv(); }
上面的代码在我简单的拉伸窗口的时候被连续执行了,若是是更复杂的DOM操做,极可能使得浏览器崩溃。其实我想要的只是在我改变完窗口大小后,再调整一次myDiv的高度。闭包
函数节流的指导思想是:某些代码能够在没有间断的状况下重复执行。第一次调用函数,建立一个定时器,在指定的时间间隔后执行代码。当第二次调用该函数的时候,它会清除前一次的定时器并设置另外一个。若是前一个定时器已经执行过了,这个操做没有意义。然而,若是前一个定时器还没有执行,其实就是替换为一个新的定时器,目的是只有在执行函数的请求中止了一段时间后才执行。app
上代码:函数
function throttle(method,context){ clearTimeout(method.tId); //{1} method.tId = setTimeout(function(){ method.call(context); //{2} },100); }
throttle方法接收两个参数:要执行的函数及在哪一个做用域中执行。{1}首先清除以前设置的任何定时器,定时器ID是储存在函数的tId属性中的。定时器代码{2}使用call来确保方法在适当的环境中执行。若是没有给出第二个参数,那么就在全局做用域内执行该方法。this
仍是上面的例子,此次window.onresize不直接执行事件处理函数了。设计
window.onresize = function(){ throttle(resizeDiv); }
这样,多数状况下,用户察觉不到变化,但可以给浏览器节省不少计算。code
关于事件节流函数的写法,网上还看到另外一种方法,能够传入延时时间做为参数,使用了闭包,可是大同小异。
原文在这里htm
function throttle(method,delay){ var timer=null; return function(){ var context=this, args=arguments; clearTimeout(timer); timer=setTimeout(function(){ method.apply(context,args); },delay); } }
同时,那篇文章的做者提到了一个新需求,我以为也挺实用,就是在函数节流的基础上间隔固定时间就执行一次。
上代码:blog
function throttle(method,delay,duration){ var timer=null, begin=new Date(); //{1} return function(){ var context=this, args=arguments, current=new Date(); clearTimeout(timer); if(current-begin>=duration){ //{2} method.apply(context,args); begin=current; }else{ timer=setTimeout(function(){ method.apply(context,args); },delay); } } }
在{1}处多设置了一个开始时间,而后在每次调用的时候判断当前时间是否有超过预设的时间间隔,若是超过了,就当即执行一次事件处理函数,而后再将当前时就按记录下来,如此往复。
在页面须要加载的图片不少的状况下,若是一次将全部的图片所有加载出来,会耗很长的时间,实际的页面呈现效果确定不会很理想,因此咱们就等到图片滚动到视口内后,再去对图片进行加载。
懒加载思路:将页面里全部img属性src属性用data-xx代替,当页面滚动直至此图片出如今可视区域时,用js取到该图片的data-xx的值赋给src。
咱们这时候首先遇到一个问题,怎么去判断图片是否是进入了视口呢?因而,JS取各类高度的方法就派上用场了。
网页可见区域宽: document.body.clientWidth; 网页可见区域高: document.body.clientHeight; 网页可见区域宽: document.body.offsetWidth (包括边线的宽); 网页可见区域高: document.body.offsetHeight (包括边线的宽); 网页正文全文宽: document.body.scrollWidth; 网页正文全文高: document.body.scrollHeight; 网页被卷去的高: document.body.scrollTop; 网页被卷去的左: document.body.scrollLeft; 网页正文部分上: window.screenTop; 网页正文部分左: window.screenLeft; 屏幕分辨率的高: window.screen.height; 屏幕分辨率的宽: window.screen.width; 屏幕可用工做区高度: window.screen.availHeight;
除了这些难记的属性以外,还要考虑各个浏览器的兼容问题,就拿网页被卷上去的高来举例
IE6/7/8/9/10:
对于没有doctype声明的页面里可使用 document.body.scrollTop 来获取 scrollTop高度 ;
对于有doctype声明的页面则可使用 document.documentElement.scrollTop ;
Safari:
safari 比较特别,有本身获取scrollTop的函数 : window.pageYOffse t;
Firefox:
直接用 document.documentElement.scrollTop ;
因此,兼容性的写法就该是:
var srcollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;
有了这些知识,一个图片懒加载的代码就能够写了,先假定文档结构以下:
<body> <ul id="mainContent"> <li><img data-src="./lazyloadimgs/0_1.png" alt="" /></li> <li><img data-src="./lazyloadimgs/0_2.png" alt="" /></li> <li><img data-src="./lazyloadimgs/0_3.png" alt="" /></li> <li><img data-src="./lazyloadimgs/mao.jpg" alt="" /></li> <li><img data-src="./lazyloadimgs/0_4.png" alt="" /></li> <li><img data-src="./lazyloadimgs/0_5.png" alt="" /></li> <li><img data-src="./lazyloadimgs/mao.jpg" alt="" /></li> <li><img data-src="./lazyloadimgs/0_6.png" alt="" /></li> </ul> </body> <style> #mainContent li{float:left;margin:15px 32px;border:1px solid #333;padding:4px;} #mainContent li img{width:300px;height:350px;} </style>
接下来就是咱们加载图片的代码了,思路就是上面所说的,当图片进入视口后,从图片的data-src中取值,而后赋给src属性,完成图片的加载。
<script> function showImg(){ var content = document.getElementById("mainContent"); var imgLen = content.children.length; var seeHeight = document.documentElement.clientHeight; //可见区域高度 var srcollTop = document.documentElement.scrollTop||window.pageYOffset||document.body.scrollTop; //滚动条距离顶部高度 for(var i = 0;i < imgLen; i++){ var curImg = content.children[i].children[0]; if(curImg.offsetTop < seeHeight + srcollTop){ if(curImg.dataset.src != "undefined"){ curImg.setAttribute("src",curImg.dataset.src); } } } } showImg(); window.onscroll = showImg(); </script>
这里onscroll被触发了好屡次,咱们不妨用一下上面提到的函数节流。使用闭包的那种写法,修改上面代码中的一处:
window.onscroll = throttle(showImg,200);
这差很少就是一个带函数节流的图片懒加载的解决方法了。