LazyLoad 延迟加载(按需加载)


Posted in Javascript onMay 31, 2010

1:实际需求

大型网站往往很矛盾,想用户在首页看到更多东西,又不想浪费太多服务器流量。比如一个有3屏的首页。可能50%的用户进首页的目的是点击首页的连接,到子页面。
那么我们的网站却为100%的用户加载了 3个 屏幕的所有内容。如果可以按需加载内容。就可以节约更多资源,做更多好的应用。

2:解决方案

用客户端语言来判断用户当前的可视范围,只加载用户可视范围的内容。最主要的是图片。因为文字信息,相对较小,其他多媒体内容相对占用服务器流量更多。
3:演示例子(最后提供)
4:解析

首先我们要分析下,这个效果会有一个 最外面的容器。他包涵了里面需要延迟加载一些内容。容器一般可能是浏览器窗口本身(window),或者一个有滚动条的DIV。
OK,我们必须获取这个容器的一些参数。比如 可视宽度,可视高度,水平卷去宽度,垂直卷去高度。我使用下面的程序。

4.1:获取容器对象属性

_this.docInfo=function(){//获取容器的相关信息 
var d={},db= (wf)? document.body : warpper, 
dd=(wf) ? document.documentElement : warpper; 
if(sys.ie){ 
d.offh=dd.offsetHeight;//可视区域H 
d.offw=dd.offsetWidth;//可视区域W 
}else{ 
if(wf){ 
d.offw=window.innerWidth;//可视区域H 
d.offh=window.innerHeight;//可视区域W 
}else{ 
d.offh=dd.offsetHeight;//可视区域H 
d.offw=dd.offsetWidth;//可视区域W 
} 
} 
d.jtop=(wf) ? db.scrollTop+dd.scrollTop : db.scrollTop ;//垂直卷去高度 
d.jleft=(wf) ? db.scrollLeft+dd.scrollLeft : db.scrollLeft;//水平卷去宽度 
//被卷去的宽度 window 使用两个相加 div的卷曲就直接使用scrollLeft就OK 
$j("bbb").innerHTML=d.offh+','+d.offw+','+d.jtop+','+d.jleft 
return d; 
} 
//注意在非IE 浏览器下 获取非window对象的可视区域 使用offsetHeight 和 offsetWidth (跟IE 一样) 
//在非IE 下获取 window对象的可视区域 则要使用 window.innerWidth 和window.innerHeight 
//也就是说在非IE 下的 window 和 非window 对象的 可视区域获取是不一样的。

4.2:获取加载内容的信息

我们主要获取加载对象距离 页面容器对象的距离 。
IE 6 7会有个BUG

wtop=sys.ie ? (sys.ie[1]=='6.0' || sys.ie[1]=='7.0') ? 0 : warpper.offsetTop : warpper.offsetTop, 
wleft=sys.ie ? (sys.ie[1]=='6.0' || sys.ie[1]=='7.0') ? 0 : warpper.offsetLeft : warpper.offsetLeft,

getoff=function(o){//获取IMG对象的 offw and offh 
o.innerHTML=(o.offsetTop-wtop) +','+ (o.offsetLeft-wleft); 
return (o.offsetTop-wtop) +','+ (o.offsetLeft-wleft); 
//注意 o.offsetTop 在chrome下要等window.onload以后才能正确获取 
};

4.3:加载主程序

他主要负责加载当前在可视范围内对象。那么我们必须去遍历所有要加载的对象。判断对象是否在当前的加载中。
然后加载他。我下面会有一个图。(方法可能不太好)

_this.Load=function(){ 
var hereline=[]; 
hereline[1]=doc.offh+doc.jtop; 
hereline[2]=doc.offw+doc.jleft; 
for(i=0;i<imgs.length;i++){ 
if(imgs[i][1] != undefined){//判断当前对象是否已经加载过 
var jj=hereline[1] - imgs[i][1] < doc.offh +130 && hereline[1] - imgs[i][1] > 0 ? true : false, 
jjj=hereline[2] - imgs[i][2] < doc.offw +270 && hereline[2] - imgs[i][2] > 0 ? true : false; 
if(jj && jjj){ 
imgall[i].innerHTML+='第'+(++j)+'个加载'; 
imgs[i][1]=undefined; 
} 
} 
} 
if( j >= imgs.length){//判断是否已经全部加载完毕 
//取消时间绑定 
alert("已经全部加载完成,程序将不再执行") 
warpper.onscroll=null; 
warpper.onresize=null; 
} 
}

我不太喜欢我的判断程序,但是暂时没找到,或者我没理解更好的算法。所以就先用这个了。
大体的意思:用容器的可视高度+容器滚动高度 - 对象距离距离容器距离 > 容器可视 + 对象本身高或宽 就证明在加载范围。(绕口令)
我们还必须把 已经加载过的对象排除在外。因为加载过的对象也满足以上公式,同时也可以少判断一些。
imgs[i][1]=undefined;
if(imgs[i][1] != undefined){//判断当前对象是否已经加载过

特别注意(看图)
LazyLoad 延迟加载(按需加载)
看上图 A B C D。 分别有4个不同的角露在了 可视范围内。所以这4个对象是需要加载的。

如果只考虑对象的某个点,或者某个线来判断对象是否在可视范围,可能带来不好的体验。

由于有上面这种情况,也给我们的编程(判断是否在可视范围内)增加了难度。

我上面的方法,是可以完成了。(如果有发现BUG ,请给我指点。其实我也有点晕了。)

最后还有几个技巧,比如

1:对象全部加载完了。就应该去掉容器对象事件触发。

2:尽量优化判断对象是否在可视范围,或者遍历的对象的算法。可以节约很多浏览器资源。

3:cloudgamer 还提到一个 延迟触发,就是快速的滑动滚动条,延迟一下也是一个小的优化。

5:推荐文章

cloudgamer 的 他讲的很详细,也比我做的要好。所以推荐去学习他的这个效果哦。很多东西我也借鉴他的。

还有就是感谢他的指点。 Lazyload 延迟加载效果
6:我的源码

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
<title>lazyload</title> 
</head> 
<body> 
<style type="text/css"> 
body{ margin:0px; padding:0; font-size:12px;} 
.jelle_box{width:270px; height:129px; border:1px solid #CCC; float:left;} 
</style> 
<input type="button" value="重新开始" onclick="lazyload().judge();" /> 
<div style="width:100%; height:500px; overflow:scroll; border:2px solid #999;" id="jelle_abcd"> 
<div id="aaa" style="width:2500px; height:800px; margin:10px;"> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
<div class="jelle_box"></div> 
</div> 
</div> 
<div style=" height:30px" id="bbb"></div> 
<script type="text/javascript"> 
(function(){ 
window.lazyload=function(){ 
var _this={},//方法集合 
imgsurl=['baidu_logo_2.gif']//最开始是用来加载图片的。这里是需要加载图片的地址集合 
imgs=[],//全部IMG 数据 格式为 [[url,offw,offh],[url,offw,offh]] 
i=0,//循环变量 
j=0,//判断当前的加载个数 
warpper=document.getElementById('jelle_abcd'),//window,//容器对象 
wf=(warpper==window) ? true : false; 
doc={offw:0,offh:0,jtop:0,jleft:0},//包含一些 容器对象当前的一些属性 
sys=(function(){//不必紧张这只是一个判断浏览器的函数,你可以使用很多方法来判断浏览器 
var ua=navigator.userAgent.toLowerCase(),sys={}; 
sys.firefox=ua.match(/firefox\/([\d\.]+)/); 
sys.ie=ua.match(/msie\s([\d\.]+)/); 
sys.chrome=ua.match(/chrome\/([\d\.]+)/); 
return sys; 
})(), 
$j=function(id){return document.getElementById(id);}, 
imgall=$j('aaa').getElementsByTagName('DIV'), 
getoff=function(o){//获取IMG对象的 offw and offh 
//alert(o.width) 
o.innerHTML=(o.offsetTop-warpper.offsetTop) +','+ (o.offsetLeft-warpper.offsetLeft); 
return (o.offsetTop-warpper.offsetTop) +','+ (o.offsetLeft-warpper.offsetLeft); 
//注意 o.offsetTop 在chrome下要等window.onload以后才能正确获取 
}; 
//o.offsetTop获取对象距离浏览器顶部的距离 必须减去外面容器的距离浏览器的距离。(如果使用window容器就不用了) 
(function(){//初始化容器对象绑定事件== 
if(wf){ 
window.onscroll=function(){_this.judge();}; 
window.onresize=function(){_this.judge();}; 
}else{ 
warpper.onscroll=function(){_this.judge();} 
warpper.onresize=function(){_this.judge();} 
} 
window.onload=function(){setTimeout(_this.judge,500);}; 
})() 
//容器对象设置结束 
for( i ; i<imgall.length ; i++ ){//初始化imgs 数组 
var arr=[],off; 
off=getoff(imgall[i]); 
//alert(off) 
arr.push(imgsurl[0]); 
arr.push((off.split(',')[0])); 
arr.push((off.split(',')[1])); 
imgs.push(arr); 
} 
_this.Load=function(){ 
var hereline=[]; 
hereline[1]=doc.offh+doc.jtop; 
hereline[2]=doc.offw+doc.jleft; 
for(i=0;i<imgs.length;i++){ 
if(imgs[i][1] != undefined){//判断当前对象是否已经加载过 
var jj=hereline[1] - imgs[i][1] < doc.offh +130 && hereline[1] - imgs[i][1] > 0 ? true : false, 
jjj=hereline[2] - imgs[i][2] < doc.offw +270 && hereline[2] - imgs[i][2] > 0 ? true : false; 
if(jj && jjj){ 
imgall[i].innerHTML+='第'+(++j)+'个加载'; 
imgs[i][1]=undefined; 
} 
} 
} 
if( j >= imgs.length){//判断是否已经全部加载完毕 
//取消时间绑定 
alert("已经全部加载完成,程序将不再执行") 
warpper.onscroll=null; 
warpper.onresize=null; 
} 
} 
_this.docInfo=function(){//获取容器的相关信息 
var d={},db= (wf)? document.body : warpper, 
dd=(wf) ? document.documentElement : warpper; 
if(sys.ie){ 
d.offh=dd.offsetHeight;//可视区域H 
d.offw=dd.offsetWidth;//可视区域W 
}else{ 
if(wf){ 
d.offw=window.innerWidth;//可视区域H 
d.offh=window.innerHeight;//可视区域W 
}else{ 
d.offh=dd.offsetHeight;//可视区域H 
d.offw=dd.offsetWidth;//可视区域W 
} 
} 
d.jtop=(wf) ? db.scrollTop+dd.scrollTop : db.scrollTop ;//垂直卷去高度 
d.jleft=(wf) ? db.scrollLeft+dd.scrollLeft : db.scrollLeft;//水平卷去宽度 
//被卷去的宽度 window 使用两个相加 div的卷曲就直接使用scrollLeft就OK 
$j("bbb").innerHTML=d.offh+','+d.offw+','+d.jtop+','+d.jleft 
return d; 
} 
//注意在非IE 浏览器下 获取非window对象的可视区域 使用offsetHeight 和 offsetWidth (跟IE 一样) 
//在非IE 下获取 window对象的可视区域 则要使用 window.innerWidth 和window.innerHeight 
//也就是说在非IE 下的 window 和 非window 对象的 可视区域获取是不一样的。 
_this.judge=function(){//后来发现不用判断方向了 
var d=_this.docInfo(); 
if( d.jtop != doc.jtop || d.jleft != doc.jleft || d.offw > doc.offw || d.offh > doc.offh){ 
//判断是否需要执行加载 
//条件为 被卷去的 y x 变化 或者 窗口大小 发生变化触发 
doc.jtop = d.jtop; 
doc.offh = d.offh; 
doc.jleft = d.jleft; 
doc.offw = d.offw; 
_this.Load();//加载程序 
} 
} 
//setTimeout(_this.judge,500);//执行初始化加载 
//setTimeout 防止onload 和 onscroll的重复执行 
//也就是本来就有onscroll的时候 最先执行了onload 
return _this; 
} 
})() 
lazyload(); 
</script> 
</body> 
</html>
Javascript 相关文章推荐
jQuery队列控制方法详解queue()/dequeue()/clearQueue()
Dec 02 Javascript
使用jquery实现放大镜效果
Sep 02 Javascript
基于Bootstrap实现图片轮播效果
May 22 Javascript
JavaScript数组去重由慢到快由繁到简(优化篇)
Aug 26 Javascript
jQuery排序插件tableSorter使用方法
Feb 10 Javascript
使用OPENLAYERS3实现点选的方法
Sep 24 Javascript
纯js实现的积木(div层)拖动功能示例
Jul 19 Javascript
JS简单实现父子窗口传值功能示例【未使用iframe框架】
Sep 20 Javascript
关于vue v-for循环解决img标签的src动态绑定问题
Sep 18 Javascript
Javascript幻灯片播放功能实现过程解析
May 07 Javascript
利用webpack理解CommonJS和ES Modules的差异区别
Jun 16 Javascript
vue a标签点击实现赋值方式
Sep 07 Javascript
基于jquery的气泡提示效果
May 31 #Javascript
niceTitle 基于jquery的超链接提示插件
May 31 #Javascript
jQuery 获取对象 根据属性、内容匹配, 还有表单元素匹配
May 31 #Javascript
jQuery 获取对象 定位子对象
May 31 #Javascript
jQuery 获取对象 基本选择与层级
May 31 #Javascript
javascript 判断数组是否已包含了某个元素的函数
May 30 #Javascript
基于jquery的inputlimiter 实现字数限制功能
May 30 #Javascript
You might like
php self,$this,const,static,-&amp;gt;的使用
2009/10/22 PHP
PHP连接SQLServer2005方法及代码
2013/12/26 PHP
thinkPHP中分页用法实例分析
2015/12/26 PHP
laravel实现上传图片,并且制作缩略图,按照日期存放的代码
2019/10/16 PHP
laravel5.6中的外键约束示例
2019/10/23 PHP
用 JSON 处理缓存
2007/04/27 Javascript
jquery 多行滚动代码(附详细解释)
2010/06/17 Javascript
不使用中间变量,交换int型的 a, b两个变量的值。
2010/10/29 Javascript
Javascript页面添加到收藏夹的简单方法
2013/08/07 Javascript
JavaScript 实现的 zip 压缩和解压缩工具包Zip.js使用详解
2015/12/14 Javascript
JavaScript数组push方法使用注意事项
2017/10/30 Javascript
React数据传递之组件内部通信的方法
2017/12/31 Javascript
vue 的keep-alive缓存功能的实现
2018/03/22 Javascript
Vue项目分环境打包的实现步骤
2018/04/02 Javascript
js实现按钮开关单机下拉菜单效果
2018/11/22 Javascript
如何提升vue.js中大型数据的性能
2019/06/21 Javascript
vue 中几种传值方法(3种)
2019/11/12 Javascript
Javascript类型判断相关例题及解析
2020/08/26 Javascript
微信小程序实现加入购物车滑动轨迹
2020/11/18 Javascript
详解Python中expandtabs()方法的使用
2015/05/18 Python
Python实现堆排序的方法详解
2016/05/03 Python
基于Python os模块常用命令介绍
2017/11/03 Python
python+opencv实现摄像头调用的方法
2019/06/22 Python
python词云库wordCloud使用方法详解(解决中文乱码)
2020/02/17 Python
matplotlib基础绘图命令之bar的使用方法
2020/08/13 Python
Python描述数据结构学习之哈夫曼树篇
2020/09/07 Python
移动端Web页面的CSS3 flex布局快速上手指南
2016/05/31 HTML / CSS
玩具反斗城西班牙网上商城:ToysRUs西班牙
2017/01/19 全球购物
锐步美国官方网站:Reebok美国
2018/01/10 全球购物
英语硕士生求职简历的自我评价
2013/10/15 职场文书
企业道德讲堂实施方案
2014/03/19 职场文书
感恩小明星事迹材料
2014/05/23 职场文书
机械工程及自动化专业求职信
2014/09/03 职场文书
2015安全保卫工作总结
2015/04/25 职场文书
舞蹈社团活动总结
2015/05/07 职场文书
CSS使用SVG实现动态分布的圆环发散路径动画
2022/12/24 HTML / CSS