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 EasyUI API 中文文档 - Dialog对话框
Nov 15 Javascript
jquery Validation表单验证使用详解
Sep 12 Javascript
使用JavaScript实现旋转的彩圈特效
Jun 23 Javascript
javascript封装 Cookie 应用接口
Aug 07 Javascript
jQuery 翻页组件yunm.pager.js实现div局部刷新的思路
Aug 11 Javascript
Vue.js 2.0学习教程之从基础到组件详解
Apr 24 Javascript
jQuery解析json格式数据示例
Sep 01 jQuery
详解微信小程序实现仿微信聊天界面(各种细节处理)
Feb 17 Javascript
详解mpvue中使用vant时需要注意的onChange事件的坑
May 16 Javascript
微信小程序页面间跳转传参方式总结
Jun 13 Javascript
jQuery 实现扁平式小清新导航
Jul 07 jQuery
微信小程序实现日历签到
Sep 21 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目录与文件操作
2011/12/30 PHP
php 读取文件夹下所有图片、文件的实例
2018/10/17 PHP
ajax无刷新动态调用股票信息(改良版)
2008/11/01 Javascript
jquery插件之信息弹出框showInfoDialog(成功/错误/警告/通知/背景遮罩)
2013/01/09 Javascript
node.js中的querystring.unescape方法使用说明
2014/12/10 Javascript
详解JavaScript ES6中的模板字符串
2015/07/28 Javascript
Angular懒加载机制刷新后无法回退的快速解决方法
2016/08/30 Javascript
js动态生成form 并用ajax方式提交的实现方法
2016/09/09 Javascript
js实现4个方向滚动的球
2017/03/06 Javascript
基于Bootstrap table组件实现多层表头的实例代码
2017/09/07 Javascript
利用JS hash制作单页Web应用的方法详解
2017/10/10 Javascript
axios使用拦截器统一处理所有的http请求的方法
2018/11/02 Javascript
Vue组件Draggable实现拖拽功能
2018/12/01 Javascript
[01:09:19]DOTA2-DPC中国联赛 正赛 VG vs Aster BO3 第二场 2月28日
2021/03/11 DOTA
python实现字符串和日期相互转换的方法
2015/05/13 Python
Python 实现数据库更新脚本的生成方法
2017/07/09 Python
Python 中的Selenium异常处理实例代码
2018/05/03 Python
python找出完数的方法
2018/11/12 Python
Django如何自定义model创建数据库索引的顺序
2019/06/20 Python
python实现LBP方法提取图像纹理特征实现分类的步骤
2019/07/11 Python
俄罗斯购买剧院和演唱会门票网站:Parter.ru
2019/11/09 全球购物
古驰英国官网:GUCCI英国
2020/03/07 全球购物
自荐信格式技巧有哪些呢
2013/11/19 职场文书
中学教师自我鉴定
2014/02/07 职场文书
给老师的一封建议书
2014/03/13 职场文书
2015年社区综治工作总结
2015/04/21 职场文书
2015年妇委会工作总结
2015/05/22 职场文书
东京审判观后感
2015/06/01 职场文书
幼儿园教师教育随笔
2015/08/14 职场文书
2016年春季运动会通讯稿
2015/11/25 职场文书
共青团优秀团员申请书(范文)
2019/08/15 职场文书
2019升学宴主持词范本5篇
2019/10/09 职场文书
基于python定位棋子位置及识别棋子颜色
2021/07/26 Python
vue实现移动端div拖动效果
2022/03/03 Vue.js
《进击的巨人》新联动CM 兵长强势出击兽巨人
2022/04/05 日漫
python实现手机推送 代码也就10行左右
2022/04/12 Python