日期:2014-05-16  浏览次数:20494 次

【补充】LazyLoad 延迟加载效果
新年期间,对上次写的LazyLoad效果做了些补充。

【resize】

先了解一下浏览器拖拉触发resize的方式。
例如在xp的系统性能选项中,设置是否“拖拉时显示窗口内容”会有不同的拖拉效果:
选择是的话,由于内容会跟着浏览器的拖拉同时渲染页面,导致resize事件的持续触发;
选择否的话,内容在拖拉完成才会渲染,并触发resize事件,即在拖拉过程中resize事件只会在确定后才触发一次;
不过ff有点特殊,即使选择否,它右下角的触发点还是会按照拖拉同时渲染页面的方式触发的。
后面测试时建议选择否,会比较准确看到结果。

再看看resize事件的支持情况。
在ie,haslayout的块级和内联元素都支持onresize事件,其他浏览器只有window对象支持。
而ie6/7跟ie8的支持程度也有不同,测试以下代码:
HTML code
<!doctype html>
<body>
<div id="show">0</div>
<div id="div" style="border:1px solid #000"></div>
<script>
var i = 0;
div.onresize = function(){ show.innerHTML = ++i; }
setTimeout('div.innerHTML="test"', 1000)
setTimeout('div.style.height="50px"', 2000)
</script>
</body>
</html>

在ie8两种情况都会触发onresize,但ie6/7只有第二种情况触发。
鉴于情况比较复杂,程序在使用window作为容器时才绑定事件,其他情况请自行设置。

resize事件有不少的问题,处理时要小心。
chrome的resize有一个问题(bug?),每次触发resize都会执行两次事件,或者说会触发两次。
而ie就复杂了,window, body和documentElement的resize会相互影响。
在ie8测试以下代码:
HTML code
 <!doctype html> 
<style>html,body{border:5px solid #06F;} </style>
<body> <div id="div" style="height:100px;"> </div> </body>
</html>
<script>
window.onresize = function(){ div.innerHTML += "window, "; }
//document.documentElement.onresize = function(){ div.innerHTML += "documentElement, "; }
//document.body.onresize = function(){ div.innerHTML += "body, "; }
</script>

当上下拖放时,onresize只会触发一次,但左右拖放时会触发两次。
换成documentElement会有差不多的结果,两个一起用的话左右拖放时documentElement会触发两次,window一次。
只设置body的话感觉就正常了,上下左右都只会触发一次。
而documentElement和body同时设置的效果跟documentElement和window的效果差不多。
如果window和body同时设置的话,后一个会覆盖前一个。
看来window和body的onresize对应的是同一个对象事件,可能为了在body设置也能做到window一样的效果。
个人推测,window和documentElement多出的一次,可能是同时触发了body的resize造成的。
ps:onresize时,用srcElement获取不到触发元素,所以确定不了是哪个元素触发。
ie7跟ie8的结果差不多,ie6就有些不同,不过估计也是盒模式的不同造成的。
具体产生原因还不清楚,这里我也很糊涂。

虽然问题弄不清楚,解决方法还是有的。

要绑定resize就是因为视框范围发生了变化,要重新设置视框范围,那么可以通过看两次resize之间视框范围有没有变化来确实是否执行程序。
在resizeDelay方法中,就是通过clientWidth和clientHeight来判断的:
HTML code
this.resizeDelay = function(){
    var clientWidth = container.clientWidth,
        clientHeight = container.clientHeight;
    if( clientWidth != width || clientHeight != height ) {
        width = clientWidth; height = clientHeight;
        oThis._delay( oThis.resize );
    }
};

ps:如果只需针对window,直接用innerHeight/innerWidth就不用理会文档模式了。


【延时加载】

一般情况下,触发程序会绑定到容器的scroll和resize事件中。
但很多时候scroll和resize会被连续触发执行,例如resize的bug,大量连续的执行大大降低效率。
为了防止无意义的连续执行,程序设置了一个_delay方法来做延时:
JScript code
var oThis = this, delay = this.delay;
if ( this._lock ) {
    this._timer = setTimeout( function(){ oThis._delay(run); }, delay );
} else {
    this._lock = true; run();
    setTimeout( function(){ oThis._lock = false; }, delay );
}


原理是用一个_lock属性,程序运行一次后_lock设为true,并用一个setTimeout延时设置它为false。
在锁定(_lock为true)期间,程序不会立即执行,达到延时的效果。
为了保证最后一次触发程序即使在锁定期间也能完成,还用了一个_timer来延时这次执行。

这种延时有什么好处,直接用setTimeout延时不是更简单方便吗?
首先直接用setTimeout只能保证最后一次程序能执行,而这种方式能保证第一次和最后一次都能执行。
直接用setTimeout更大的问题是,如果持续触发,会导致程序一直不能执行(前提是执行时有正确clear掉定时器),而这种方式能保证程序在时间段内执行一次。

还有一个方法是不绑定事件,只用setTimeout或setInterval来监听。
好处是没有连续触发的问题,也不会有resize的bug