lazyload延迟加载组件

lazyload现在网上已经用的很多(淘宝商城,新浪微博等等),先放demo:mylazyLoad.zip 

效果:

<div id="redbox1" οnclick="alert('js执行了')" class="redbox">点击我</div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>
<div class="redbox"></div>
<div class="bluebox"></div>

【基本原理】

在有大量数据加载的页面中,我们需要一个容器,这个容器可以是浏览器窗口,也可以是页面中的一个容器,在页面加载的时候,我们可以将这容器显示之外的内容阻止其加载,当我们滚动这个容器到相应区域的时候才将该区域内容加载出来,以此达到加快浏览速度的目的。

延迟加载一般分静态和动态两种。

静态的典型例子就是淘宝商城,我们在观察淘宝商城的html时发现,其页面采用了大量的textarea来存放页面元素,我们想要把页面元素存放在html里,而又不想这些元素被解析,同时又能轻松方便的获取,textarea正好满足了这些条件(真不知道哪位牛人想到的)。

而动态的可以说就是ajax获取数据再绑定,典型例子就是新浪微博,当页面滚动到底部时触发加载函数。

我的理解是将需要延迟加载的触发元素存入一个数组中,当容器scroll/resize时遍历这个数组,如果触发元素在视窗范围内就执行加载函数,并将这个元素从数组中删除来提高效率。

【程序说明】

一般在创建实例的时候,需要定义两个属性:elems和container,elems是触发加载的元素集合,或者说是加载内容集合,container就是容器。

1.因为elems需要进行删除,所以首先需要将elems转换为数组Array。

ExpandedBlockStart.gif $A() code
/* 将参数转换为数组
 * @param {all} a 参数
 
*/
var  $A = function (a){
    
if ( ! a) return  [];
    
if (a  instanceof  Array)  return  a;
    
var  arr = [],len = a.length;
    
if ( / string|number / .test( typeof  a) || instanceof  Function  ||  len === undefined){
        arr[
0 ] = a;
    }
else {
        
for ( var  i = 0 ;i < len;i ++ ){
            arr[i]
= a[i];
        }
    }
    
return  arr;    
}
this .elems = $A( this .options.elems); /* 加载对象转换成数组 */

参数a可以是string,number,object,array,function,HTMLCollection,null。

PS:我发现object.length===undefined

2.container是容器,可以是window,也可以是页面元素,所以初始化时先判断是否是window。

ExpandedBlockStart.gif container
var  doc = document;
var  isWin  =  c == window || c == doc || c == null ||! c.tagName || / body|html / i.test(c.tagName); /* 判断容器是否是window */
if (isWin)c = doc.documentElement;
this .container = c;

如果不是明确的非body/html的dom元素,将都视为容器为window。

3.获取container的显示范围,即容器相对于浏览器视窗左上角的top/bottom/left/right距离,如果是window,即浏览器视窗的大小。

ExpandedBlockStart.gif getContainerRange
/* 获取容器显示范围方法 */
var  _getContainerRange = isWin && window.innerWidth ? function (){
    
return  {top: 0 ,left: 0 ,right:window.innerWidth,bottom:window.innerHeight}
}:
function (){
    
return  _this._getRect(c);
}
this ._refreshRange = function (){
    _this.range
= _getContainerRange();
}
this ._refreshRange();

/* 获取元素位置参数 */
_getRect:
function (elem){
    
var  r = elem.getBoundingClientRect(); /* 元素到窗口左上角距离 */
    
return  {top:r.top,left:r.left,bottom:r.bottom,right:r.right}
}

 获取浏览器视窗大小在IE下可以通过offsetWidth/offsetHeight获得,但非IE浏览器则略有差异,好在非IE下有innerWidth/innerHeight可以使用。另外还有dom.getBoundingClientRect()这个方法,它的作用是获得dom相对于视窗左上角的top/left/bottom/right距离,而且这个方法已经得到所有浏览器的支持,大大提高了我们的效率。

PS:getBoundingClientRect可以看下http://www.cnblogs.com/qieqing/archive/2008/10/06/1304399.html ,另外谈到IE下2px问题,因为这里我都是用这个方法来获取容器以及元素位置,所以这2px差异可以忽略,也许这不太严谨,但同时也为了提高点效率考虑。

4.接下来就是给container绑定scroll/resize事件。

ExpandedBlockStart.gif addEventHandler
this ._scrollload = function (){
    
if ( ! isWin){_this._refreshRange();}
    _this._doLoad();
}
this ._noWinScroll = function (){  /* 解决刷新时window滚动条定位后造成range错误bug */
    _this.range
= _getContainerRange();
    removeEventHandler(window,
" scroll " ,_this._noWinScroll);
}
this ._resizeload = function (){
    _this._refreshRange();
    _this._doLoad();
}
this .binder  =  isWin  ?  window : c;
if ( ! isWin)addEventHandler(window, " scroll " , this ._noWinScroll);
addEventHandler(
this .binder, " scroll " , this ._scrollload);
addEventHandler(
this .binder, " resize " , this ._resizeload);

container是window话,scroll是不会改变container的range的,而container是元素的话,range会随着window的scroll而改变,所以这里做分支处理。

另外当我们讲window的滚动条滚至中间位置,再F5刷新页面之后,滚动条同样会在刚刚的位置,IE下首先会将滚动条提至顶部,再定位到刚刚的位置,这样就出现了一个问题,在第一次执行lazyload的时候,container的range是window滚动条在顶部时候的值,而并非我们需要的实际值。所以我这里在非isWin情况下,给window绑定一次_noWinScroll事件,滚动条定位后删除这个事件。

5.下面讲的是具体的判断、执行

ExpandedBlockStart.gif _doLoad
/* 加载判断,防止多次调用
 @lock锁定,加载过程中锁定。如果为false,执行加载;如果为true,延迟递归
*/
_doLoad:
function (){
    
var  _this = this ;
    
if ( ! this .lock){
        
this .lock = true ;
        setTimeout(
function (){_this._loadRun()}, 100 );
    }
else {
        clearTimeout(
this .timer);
        
var  self = arguments.callee;
        
this .timer = setTimeout( function (){self.call(_this)}, 100 );
    }
}

scroll事件在各个浏览器里的执行次数不同,最好的是FF,一次滚轮滚动只会执行一次,而其他浏览器或多或少的会执行多次。为了优化这点,用了一个lock判断,当在进行加载函数_loadRun时,我们锁住scroll,具体方法就是"clearTimeout(this.timer);this.timer=setTimeout(function(){_this._doLoad();},100);",使用setTimeout延迟递归_doLoad,执行第二次递归前clearTimeout上一次,这样就能保证一次滚动只执行两次,开始一次,结束一次,中间的全部锁住。

ExpandedBlockStart.gif _loadRun
/* 加载运行 */
_loadRun:
function (){
    
var  elems = this .elems;
    
if (elems.length){
        
for ( var  i = 0 ;i < elems.length;i ++ ){
            
var  rect = this ._getRect(elems[i]);
            
var  side = this ._isRange( this ._inRange(rect));
            
if (side && side != 0 ){
                
if (side == 1 &&! this .elock){
                    
this .elock = true ;
                    
this ._onDataLoad(elems[i]);
                    elems.splice(i
-- , 1 ); /* 加载完之后将该对象从队列中删除 */
                }
else { break ;}
            }
        }            
        
if ( ! elems.length){
            
this ._release();
        }
    }
    
this .lock = false ;
}
_inRange:function(rect){
    
var range=this.range;
    
var side={
        v : rect.top
<=range.bottom ? rect.bottom>=range.top ? "in" : "" : "bottom",/*垂直位置*/
        h : rect.left
<=range.right ? rect.right>=range.left ? "in" : "" : "right" /*水平位置*/
    };
    
return side;
},
_isRange:
function(side){
    
/*1:加载 -1:跳出循环 0:不加载执行下一个*/
    
return {
        v:side.v 
? side.v=="in"?1:-1 : 0,
        h:side.h 
? side.h=="in"?1:-1 : 0,
        c:side.v
&&side.h ? side.v=="in"&&side.h=="in"? 1:side.v!="in"?-1:0 : 0
    }[
this.mode||"c"]
}

这里的逻辑如下:

1)遍历elems数组,获取元素[0]的rect,根据rect和container的range做比较,判断元素[0]相对于container的位置("[top/left]","bottom/right","in")

                

2)再根据我们的mode获得操作类型(-1,0,1)。-1表示在容器显示范围的后面,之后的元素可以不再判断,执行跳出循环;0表示在容器显示范围的前面,不执行加载,进行下个元素的判断;1表示在显示范围内,需要加载。

3)当返回的side为1时,执行_onDataLoad(),然后从元素集合中删除该元素,用的方法是Array.splice(index,num),同时i--,使得能准确的找到下个元素。这里的elock用来锁定元素加载,主要用在动态ajax加载的时候,因为动态加载的时候,我们希望当多个元素同时存在于container以及多次触发scroll时,只执行第一个元素的加载。

4)然后进行下个元素[1]的判断,重复之前的步骤。

5)当元素集合为空时,摧毁所有的绑定。

ExpandedBlockStart.gif _release
_release: function (){
    removeEventHandler(
this .binder, " scroll " , this ._scrollload);
    removeEventHandler(
this .binder, " resize " , this ._resizeload);
        
this ._onDataEnd();
}

6._onDataLoad默认情况是静态加载。

ExpandedBlockStart.gif _onDataLoad
/* 删除Script字符串内容 */
String.prototype.removeJS
= function (){
    
return   this .replace( / <script[^>]*?>([\w\W]*?)<\/script> / ig, "" );
}
/* 将Script字符串转换为Script对象,返回Script or false */
String.prototype.getJS
= function (){
    
var  js = this .replace( / [\s\S]*?<script[^>]*?>([\w\W]*?)<\/script>[\s\S]*? / g, " $1\r " );
    
if (js == this ){
        
return   false ;
    }
else {
        
var  s = document.createElement( " script " );
        s.text
= js;
        
return  s;
    }
}
this ._onDataLoad = this .options.ondataload  ||   function (elem){  /* 数据加载 */
    
var  h = elem.getElementsByTagName( " textarea " );
    
if (h.length){
        
var  js = h[ 0 ].value.getJS();  /* 解决innerHTML javascript不执行的问题 */
        
if (js){
            elem.innerHTML
= h[ 0 ].value.removeJS();  /* 删除javascript字符串 */
            elem.appendChild(js);
        }
else {
            elem.innerHTML
= h[ 0 ].value;
        }
    }
    
this .elock = false ;
}

这里主要说的是html里的javascript代码问题,通过innerHTML的javascript代码是不会执行的,所以在这里需要提取html里的script代码,创建一个script元素,appendChild进容器内才能执行。

【总结】

总的来说在优化上以及需求的考虑上都有了提高。也越来越喜欢用自己整理的框架去组件,这样就能做到不仅知其然而且还能知其所以然。希望今后能在算法上得到指点。

demo丑了点,大家凑合凑合(^。^)y-~~

【完整代码】

ExpandedBlockStart.gif javascript library
//  JavaScript Document
/*
my javascript library v1.2 */
/* written by Lecaf */
/* update by 2011.4.12 */

/* 删除Script字符串内容 */
String.prototype.removeJS
= function (){
    
return   this .replace( / <script[^>]*?>([\w\W]*?)<\/script> / ig, '' );
}
/* 将Script字符串转换为Script对象,返回Script or false */
String.prototype.getJS
= function (){
    
var  js = this .replace( / [\s\S]*?<script[^>]*?>([\w\W]*?)<\/script>[\s\S]*? / g, ' $1\r ' );
    
if (js == this ){
        
return   false ;
    }
else {
        
var  s = document.createElement( ' script ' );
        s.text
= js;
        
return  s;
    }
}

/* getElementById
 * @param {String} id ID值
 
*/
var  $id  =   function (id){
    
if ( typeof  id != ' undefined '   &&   typeof  id  ===   ' string ' ){
        
return  document.getElementById(id);    
    }
    
return   null ;
}

/* 讲参数转换为数组
 * @param {all} a 参数
 
*/
var  $A = function (a){
    
if ( ! a) return  [];
    
if (a  instanceof  Array)  return  a;
    
var  arr = [],len = a.length;
    
if ( / string|number / .test( typeof  a) || instanceof  Function  ||  len === undefined){
        arr[
0 ] = a;
    }
else {
        
for ( var  i = 0 ;i < len;i ++ ){
            arr[i]
= a[i];
        }
    }
    
return  arr;    
}

/* 注销事件
 * @param {Object} oTarget 对象
 * @param {String} sEventType 事件类型
 * @param {Function} fnHandler 事件方法
 
*/
var  removeEventHandler = function (oTarget, sEventType, fnHandler) {
    
if (oTarget.listeners[sEventType]){
        
var  listeners = oTarget.listeners[sEventType];
        
for ( var  i = 0 ,fn;fn = listeners[i ++ ];){
            
if (fn == fnHandler){
                listeners.splice(
-- i, 1 );
            }
        }
        
if ( ! listeners.length && listeners[ " _handler " ]){
            oTarget.removeEventListener 
?  oTarget.removeEventListener(sEventType, listeners[ " _handler " ],  false ) : oTarget.detachEvent( ' on '   +  sEventType, listeners[ " _handler " ]);
        }
    }    
}

/* 添加事件
 * @param {Object} oTarget 对象
 * @param {String} sEventType 事件类型
 * @param {Function} fnHandler 事件方法
 
*/
var  addEventHandler = function (oTarget, sEventType, fnHandler) {
    oTarget.listeners
= oTarget.listeners || {};
    
var  listeners  =  oTarget.listeners[sEventType]  =  oTarget.listeners[sEventType] || [];
    listeners.push(fnHandler);
    
if ( ! listeners[ " _handler " ]){
        listeners[
" _handler " ] = function (e){
            
var  e = e || window.event;
            
for ( var  i = 0 ,fn;fn = listeners[i ++ ];){
                fn.call(oTarget,e)
            }
        }
        oTarget.addEventListener 
?  oTarget.addEventListener(sEventType, listeners[ " _handler " ],  false ) : oTarget.attachEvent( ' on '   +  sEventType, listeners[ " _handler " ]);
    }    
}

/* 触发事件
 * @param {Object} oTarget 对象
 * @param {String} sEventType 事件类型
 
*/
var  dispatchEventHandler = function (oTarget,sEventType){
    
if (oTarget.dispatchEvent){
        
var  e = document.createEvent( ' Event ' );
        e.initEvent(sEventType,
true , true );
        oTarget.dispatchEvent(e);
    }
else {
        oTarget.fireEvent(
' on ' + sEventType);
    }
}

/* json扩展
 * @param {Object} target 目标json
 * @param {Object} src 源json
 
*/
var  extendJson = function (target,src){
    
for ( var  para  in  src){
        target[para]
= src[para];
    }
    
return  target;
}

/* 在目标元素之后插入新元素 js自带方法: target.appendChild(newDoc);target.insertBefore(newDoc,existingChild);
 * @param {Document} newEl 新元素
 * @param {Document} targetEl 目标元素
 
*/
var  insertAfter = function (newEl,targetEl){
    
var  parentEl  =  targetEl.parentNode;
    
if (parentEl.lastChild  ==  targetEl){
        parentEl.appendChild(newEl);
    }
else {
        parentEl.insertBefore(newEl,targetEl.nextSibling);
    }
}

/* 动态加载CSS文件
 * @param {String} file css路径
 * @param {String} cssid css link ID
 
*/
var  loadCSS = function  (file,cssid){
    
var  cssTag  =  cssid  ?  document.getElementById(cssid) :  null ;
    
var  head  =  document.getElementsByTagName( ' head ' ).item( 0 );
    
if (cssTag) head.removeChild(cssTag);
    css 
=  document.createElement( ' link ' );
    css.href 
=  file;
    css.rel 
=   ' stylesheet ' ;
    css.type 
=   ' text/css ' ;
    
if (cssid){css.id  =  cssid;}
    head.appendChild(css);
}

/* ajax封装
 * @param {Object} options 参数集
 * @param {String} url 链接
 * @param {String} type 传参方式 'POST' or 'GET'(默认)
 * @param {Bool} async 是否异步 true异步(默认) false同步
 * @param {String} dataType 返回数据类型 'html'(默认) 'xml' 'json'
 * @param {Function} beforeSend 发送请求前调用函数
 * @param {Function} success 请求成功后回调函数
 * @param {Function} complete 请求完成后回调函数(不管成功与否)
 
*/
var  ajaxFun  =   function (options){
    
var  ajaxops = {
        url:
'' ,
        type:
' GET ' ,
        async:
true ,
        dataType:
' html ' ,
        beforeSend:
null ,
        success:
function (){},
        complete:
null
    }
    
var  ajaxops  =  extendJson(ajaxops,options);
    
if (ajaxops.url){
        
var  xmlHttp;
    
        
try {
            
//  Firefox, Opera 8.0+, Safari
            xmlHttp = new  XMLHttpRequest();
        }
catch  (e){
            
//  Internet Explorer
             try {
                xmlHttp
= new  ActiveXObject( ' Msxml2.XMLHTTP ' );
            }
catch  (e){
                
try {
                    xmlHttp
= new  ActiveXObject( ' Microsoft.XMLHTTP ' );
                }
catch  (e){
                    alert(
' 您的浏览器不支持AJAX! ' );
                    
return   false ;
                }
            }
        }
        
var  requestDone = false ;
        
        
if ( ! ajaxops.async && navigator.userAgent.indexOf( ' Firefox ' ) > 0 ){
            xmlHttp.onload
= function (){
                
if (( xmlHttp.status  >=   200   &&  xmlHttp.status  <   300  )  ||  xmlHttp.status  ===   304   ||  xmlHttp.status  ===   1223   ||  xmlHttp.status  ===   0 ){
                    
var  msg;
                    
switch (ajaxops.dataType){
                        
case   ' html ' :
                            msg
= xmlHttp.responseText;
                            
break ;
                        
case   ' xml ' :
                            msg
= xmlHttp.responseXML;
                            
break ;
                        
case   ' json ' :
                            msg
= xmlHttp.responseText;
                            msg
= ( new  Function( ' return  ' + msg))();
                            
break ;
                        
default :
                            msg
= xmlHttp.responseText;
                            
break ;
                    }
                    ajaxops.success(msg);
                }
                
if (ajaxops.complete  &&   ! requestDone){
                    ajaxops.complete(msg);
                    requestDone
= true ;
                }
            }
        }
else {
            xmlHttp.onreadystatechange
= function (){        
                
if (xmlHttp.readyState === 4 ){
                    
if (( xmlHttp.status  >=   200   &&  xmlHttp.status  <   300  )  ||  xmlHttp.status  ===   304   ||  xmlHttp.status  ===   1223   ||  xmlHttp.status  ===   0 ){
                        
var  msg;
                        
switch (ajaxops.dataType){
                            
case   ' html ' :
                                msg
= xmlHttp.responseText;
                                
break ;
                            
case   ' xml ' :
                                msg
= xmlHttp.responseXML;
                                
break ;
                            
case   ' json ' :
                                msg
= xmlHttp.responseText;
                                msg
= ( new  Function( ' return  ' + msg))();
                                
break ;
                            
default :
                                msg
= xmlHttp.responseText;
                                
break ;
                        }
                        ajaxops.success(msg);
                    }
                    
if (ajaxops.complete  &&   ! requestDone){
                        ajaxops.complete(msg);
                        requestDone
= true ;
                    }
                }
            }
        }
        
if (ajaxops.beforeSend){
            ajaxops.beforeSend();
        }
        xmlHttp.open(ajaxops.type,ajaxops.url,ajaxops.async);
        xmlHttp.send(
null );
    }
}

/*
 * $class 写类工具函数
 * @param {Function} constructor
 * @param {Object} prototype
 * write by Snandy http://www.cnblogs.com/snandy/
 
*/
var  $class  =   function (constructor,prototype) {
    
var  c  =  constructor  ||   function (){};
    
var  p  =  prototype  ||  {};
    
return   function () {        
        
for ( var  atr  in  p) {
            arguments.callee.prototype[atr] 
=  p[atr];
        }            
        c.apply(
this ,arguments);
    }
}
ExpandedBlockStart.gif lazyload
//  JavaScript Document
/*
Lazyload v1.2 */
/* written by Lecaf */
/* update by 2011.4.8 */
var  Lazyload = function (options){
    
this ._init(options); /* 初始化 */
    
this ._doLoad(); /* 第一次加载 */
    
if ( ! this .elems.length) this ._release(); /* 如果加载元素为空,释放 */
}
var  proto = {
    
/* 初始化参数 */
    _init:
function (options){
        
this .binder = null /* 加载容器对象 */
        
this .range = {};  /* 加载容器显示范围 */
        
this .elems = []; /* 加载对象队列 */
        
this .container = null ;
        
this .mode = "" ;
        
this .lock = false ; /* 加载容器锁定 */
        
this .elock = false ; /* 加载元素锁定 */
        
this .timer = null ; /* _doLoad计时器 */
        
this .options = /* 定制参数 */
            container:window,
/* 加载容器 */
            elems:
null , /* 加载数据集合 */
            mode:
" v " , /* 加载模式 v(垂直加载) h(水平加载) c(交叉加载) 默认v */
            ondataload:
null , /* 数据加载方式 */
            ondataend:
function (){} /* 数据加载完毕 */
        }
        extendJson(
this .options,options || {});
        
this .elems = $A( this .options.elems); /* 加载对象转换成数组 */
        
this .mode = this .options.mode;
        
this ._onDataLoad = this .options.ondataload  ||   function (elem){  /* 数据加载 */
            
var  h = elem.getElementsByTagName( " textarea " );
            
if (h.length){
                
var  js = h[ 0 ].value.getJS();  /* 解决innerHTML javascript不执行的问题 */
                
if (js){
                    elem.innerHTML
= h[ 0 ].value.removeJS();  /* 删除javascript字符串 */
                    elem.appendChild(js);
                }
else {
                    elem.innerHTML
= h[ 0 ].value;
                }
            }
            
this .elock = false ;
        }
        
this ._onDataEnd = this .options.ondataend;  /* 所有内容加载完执行 */
        
this ._initContainer( this .options.container); /* 初始化容器 */
    },
    
/* 初始化容器 */
    _initContainer:
function (c){
        
var  doc = document;
        
var  _this = this ;
        
var  isWin  =  c == window || c == doc || c == null ||! c.tagName || / body|html / i.test(c.tagName); /* 判断容器是否是window */
        
if (isWin)c = doc.documentElement;
        
this .container = c;
        
/* 获取容器显示范围方法 */
        
var  _getContainerRange = isWin && window.innerWidth ? function (){
            
return  {top: 0 ,left: 0 ,right:window.innerWidth,bottom:window.innerHeight}
        }:
function (){
            
return  _this._getRect(c);
        }
        
this ._refreshRange = function (){
            _this.range
= _getContainerRange();
        }
        
this ._refreshRange();
        
this ._scrollload = function (){
            
if ( ! isWin){_this._refreshRange();}
            _this._doLoad();
        }
        
this ._noWinScroll = function (){  /* 解决刷新时window滚动条定位后造成range错误bug */
            _this.range
= _getContainerRange();
            removeEventHandler(window,
" scroll " ,_this._noWinScroll);
        }
        
this ._resizeload = function (){
            _this._refreshRange();
            _this._doLoad();
        }
        
this .binder  =  isWin  ?  window : c;
        
if ( ! isWin)addEventHandler(window, " scroll " , this ._noWinScroll);
        addEventHandler(
this .binder, " scroll " , this ._scrollload);
        addEventHandler(
this .binder, " resize " , this ._resizeload);
    },
    
/* 获取元素位置参数 */
    _getRect:
function (elem){
        
var  r = elem.getBoundingClientRect(); /* 元素到窗口左上角距离 */
        
return  {top:r.top,left:r.left,bottom:r.bottom,right:r.right}
    },
    
/* 加载判断,防止多次调用
     @lock锁定,加载过程中锁定。如果为false,执行加载;如果为true,延迟递归
    
*/
    _doLoad:
function (){
        
var  _this = this ;
        
if ( ! this .lock){
            
this .lock = true ;
            setTimeout(
function (){_this._loadRun()}, 100 );
        }
else {
            clearTimeout(
this .timer);
            
var  self = arguments.callee;
            
this .timer = setTimeout( function (){self.call(_this)}, 100 );
        }
    },
    
/* 加载运行 */
    _loadRun:
function (){
        
var  elems = this .elems;
        
if (elems.length){
            
for ( var  i = 0 ;i < elems.length;i ++ ){
                
var  rect = this ._getRect(elems[i]);
                
var  side = this ._isRange( this ._inRange(rect));
                
if (side && side != 0 ){
                    
if (side == 1 &&! this .elock){
                        
this .elock = true ;
                        
this ._onDataLoad(elems[i]);
                        elems.splice(i
-- , 1 ); /* 加载完之后将该对象从队列中删除 */
                    }
else { break ;}
                }
            }            
            
if ( ! elems.length){
                
this ._release();
            }
        }
        
this .lock = false ;
    },
    
/* 判断对象相对容器位置 */
    _inRange:
function (rect){
        
var  range = this .range;
        
var  side = {
            v : rect.top
<= range.bottom  ?  rect.bottom >= range.top  ?   " in "  :  ""  :  " bottom " , /* 垂直位置 */
            h : rect.left
<= range.right  ?  rect.right >= range.left  ?   " in "  :  ""  :  " right "   /* 水平位置 */
        };
        
return  side;
    },
    _isRange:
function (side){
        
/* 1:加载 -1:跳出循环 0:不加载执行下一个 */
        
return  {
            v:side.v 
?  side.v == " in " ? 1 : - 1  :  0 ,
            h:side.h 
?  side.h == " in " ? 1 : - 1  :  0 ,
            c:side.v
&& side.h  ?  side.v == " in " && side.h == " in " ?   1 :side.v != " in " ?- 1 : 0  :  0
        }[
this .mode || " c " ]
    },
    
/* 释放 */
    _release:
function (){
        removeEventHandler(
this .binder, " scroll " , this ._scrollload);
        removeEventHandler(
this .binder, " resize " , this ._resizeload);
        
this ._onDataEnd();
    }
}

window.onload
= function (){
    
var  Divload = $class(Lazyload,proto);
    
var  divload = new  Divload({
        elems:document.getElementById(
" loadmain " ).getElementsByTagName( " div " ),
        container:$id(
" loadbox " ),
        mode:
" c "
    });
    
var  Winload = $class(Lazyload,proto);
    
var  winload = new  Winload({
        elems:$id(
" ajaxbox " ).getElementsByTagName( " div " ),
        container:window,
        ondataload:
function (elem){        
            
var  othis = this ;
            ajaxFun({
                url:
" ajax.html " ,
                beforeSend:
function (){
                    elem.getElementsByTagName(
" p " )[ 0 ].style.display = "" ;
                },
                success:
function (msg){
                    
var  box = document.getElementById( " ajaxload " );
                    box.innerHTML
= box.innerHTML + msg;
                },
                complete:
function (){
                    othis.elock
= false ;
                }
            })
        }
    })
}

转载于:https://www.cnblogs.com/lecaf/archive/2011/04/08/lazyload.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值