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 相关文章推荐
IE中createElement需要注意的一个问题
Jul 13 Javascript
jquery 插件开发备注
Aug 27 Javascript
JS实现从表格中动态删除指定行的方法
Mar 31 Javascript
js中日期的加减法
May 06 Javascript
jQuery模拟select实现下拉菜单功能
Jun 20 Javascript
jquery的checkbox,radio,select等方法小结
Aug 30 Javascript
浅谈jQuery添加的HTML,JS失效的问题
Oct 05 Javascript
jQuery EasyUI右键菜单实现关闭标签/选项卡
Oct 10 Javascript
用headjs来管理和加载js 提高网站加载速度
Nov 29 Javascript
es6学习之解构时应该注意的点
Aug 29 Javascript
JS+CSS实现随机点名(实例代码)
Nov 04 Javascript
vue-resourc发起异步请求的方法
Feb 11 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 处理上百万条的数据库如何提高处理查询速度
2010/02/08 PHP
php二维数组用键名分组相加实例函数
2013/11/06 PHP
PHP后端银联支付及退款实例代码
2017/06/23 PHP
由php中字符offset特征造成的绕过漏洞详解
2017/07/07 PHP
visual studio code 调试php方法(图文详解)
2017/09/15 PHP
js实现ASP分页函数 HTML分页函数
2006/09/22 Javascript
javascript在事件监听方面的兼容性小结
2010/04/07 Javascript
node.js中的events.emitter.listeners方法使用说明
2014/12/10 Javascript
究竟什么是Node.js?Node.js有什么好处?
2015/05/29 Javascript
javascript作用域链(Scope Chain)用法实例解析
2015/11/30 Javascript
jqueryMobile 动态添加元素,展示刷新视图的实现方法
2016/05/28 Javascript
javascript jquery对form元素的常见操作详解
2016/06/12 Javascript
第一次接触神奇的Bootstrap表单
2016/07/27 Javascript
使用snowfall.jquery.js实现爱心满屏飞的效果
2017/01/05 Javascript
Vuex简单入门
2017/04/19 Javascript
令按钮悬浮在(手机)页面底部的实现方法
2017/05/02 Javascript
easyUI下拉列表点击事件使用方法
2017/05/18 Javascript
使用jQuery.Pin垂直滚动时固定导航
2017/05/24 jQuery
JavaScript事件发布/订阅模式原理与用法分析
2018/08/21 Javascript
如何解决React官方脚手架不支持Less的问题(小结)
2018/09/12 Javascript
Python3实现连接SQLite数据库的方法
2014/08/23 Python
python文件操作整理汇总
2014/10/21 Python
Python实现二分查找与bisect模块详解
2017/01/13 Python
python爬虫获取多页天涯帖子
2018/02/23 Python
python实现控制COM口的示例
2019/07/03 Python
Python 中如何实现参数化测试的方法示例
2019/12/10 Python
python爬取招聘要求等信息实例
2020/11/20 Python
全球领先的各类汽车配件零售商:Advance Auto Parts
2016/08/26 全球购物
英国当代时尚和街头服饰店:18montrose
2018/12/15 全球购物
UML设计模式笔试题
2014/06/07 面试题
《青山处处埋忠骨》教学反思
2014/04/22 职场文书
公共场所禁烟标语
2014/06/25 职场文书
化工专业求职信
2014/07/01 职场文书
学生自我鉴定格式及范文
2014/09/16 职场文书
工伤私了协议书范本
2014/11/24 职场文书
Python面试不修改数组找出重复的数字
2022/05/20 Python