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 相关文章推荐
js过滤数组重复元素的方法
Sep 05 Javascript
基于jquery的仿百度搜索框效果代码
Apr 11 Javascript
javascript获取ckeditor编辑器的值(实现代码)
Nov 18 Javascript
jQuery aminate方法定位到页面具体位置
Dec 26 Javascript
node.js中的fs.lstatSync方法使用说明
Dec 16 Javascript
$.extend 的一个小问题
Jun 18 Javascript
js带缩略图的图片轮播效果代码分享
Sep 14 Javascript
request请求获取参数的实现方法(post和get两种方式)
Sep 27 Javascript
vue.js打包之后可能会遇到的坑!
Jun 03 Javascript
使用electron将vue-cli项目打包成exe的方法
Sep 29 Javascript
zepto.js 实时监听输入框的方法
Dec 04 Javascript
微信小程序如何实现全局重新加载
Jun 05 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
在Windows系统上安装PHP运行环境文字教程
2010/07/19 PHP
yii框架配置默认controller和action示例
2014/04/30 PHP
PHP命名空间简单用法示例
2018/12/28 PHP
php常用字符串查找函数strstr()与strpos()实例分析
2019/06/21 PHP
js null undefined 空区别说明
2010/06/13 Javascript
javascript预览上传图片发现的问题的解决方法
2010/11/25 Javascript
jquery ready(fn)事件使用介绍
2013/08/21 Javascript
javascript针对不确定函数的执行方法
2015/12/16 Javascript
基于jQuery制作小图标上下滑动特效
2017/01/18 Javascript
vue 封装自定义组件之tabal列表编辑单元格组件实例代码
2017/09/07 Javascript
基于JavaScript实现瀑布流布局
2018/08/15 Javascript
vue 登录滑动验证实现代码
2018/08/24 Javascript
Element UI 自定义正则表达式验证方法
2018/09/04 Javascript
jQuery实现获取及设置CSS样式操作详解
2018/09/05 jQuery
浅谈angular表单提交中ng-submit的默认使用方法
2018/09/30 Javascript
基于vue通用表单解决方案的思考与分析
2019/03/16 Javascript
浅谈js闭包理解
2019/04/01 Javascript
微信小程序如何使用globalData的方法
2019/06/06 Javascript
layui复选框的全选与取消实现方法
2019/09/02 Javascript
编写一个javascript元循环求值器的方法
2020/04/14 Javascript
vue动态加载SVG文件并修改节点数据的操作代码
2020/08/17 Javascript
Python中格式化format()方法详解
2017/04/01 Python
python实现对csv文件的列的内容读取
2018/07/04 Python
python SocketServer源码深入解读
2019/09/17 Python
jupyter notebook tensorflow打印device信息实例
2020/04/20 Python
What is view? why do we have view?
2012/06/22 面试题
Java的类可以定义为Protected或者Private得吗
2015/09/25 面试题
党员自我评价分享
2013/12/13 职场文书
妇女儿童发展规划实施方案
2014/03/16 职场文书
乡镇干部党的群众路线教育实践活动个人对照检查材料
2014/09/24 职场文书
2015暑假社会调查报告
2015/07/13 职场文书
2015年十月一日放假通知
2015/08/18 职场文书
《小摄影师》教学反思
2016/02/18 职场文书
2016五四青年节活动总结范文
2016/04/06 职场文书
单身狗福利?Python爬取某婚恋网征婚数据
2021/06/03 Python
SpringMVC 整合SSM框架详解
2021/08/30 Java/Android