周梦康 发表于 2014-06-28 3990 次浏览 标签 : jquerylazyload

由于前天正好给自己博客添加了懒加载的功能,所以就顺便学下别人的代码。下面是我的笔记。

防止别人代码如后更新,我复制了一份,代码地址:https://github.com/zhoumengkang/zhoumengkang.github.io/blob/master/code-demo/jquery.lazyload/jquery.lazyload.js

先了解下jquery对象级别的插件的一般书写格式:

(function($) {
    $.fn.pluginName = function() {
        // Our plugin implementation code goes here.   
    };
})(jQuery);

简单演示:

<!doctype html>
<html>
<head lang="zh-cn">
    <meta charset="utf-8">
    <title></title>
    <script type="text/javascript" src="http://libs.baidu.com/jquery/1.7.2/jquery.min.js"></script>
    <style type="text/css">
        div{
            background-color: #bbb;
        }
    </style>
</head>
<body>
<div style="height:400px"></div>
</body>
<script type="text/javascript">
    (function($) {
        $.fn.changeColor = function(color) {
               $(this).css('background',color);
        };
    })(jQuery);

    $("div").click(function(){
        $(this).changeColor('red');
    })

</script>
</html>

为什么要这样写呢?

首先以一个匿名函数的形式,把jquery对象作为一个实参传入该闭包,然后通过jquery.fn.changeColor给jquery原型链添加了一个changeColor的方法。这样所有jquery对象都可以调用该方法。

查看jquery的源代码:https://github.com/jquery/jquery/blob/master/src/core.js 发现jQuery.fn = jQuery.prototype

查看lazyload的大体结构:

拆轮子之 jquery.lazyload.js 完全解析,顺便学习下 jquery 插件的开发流程 [可读性可能不高,自己笔记]

在这个匿名函数里,设置了四个形参,最后一个可以忽略,前三个分别为jQuery对象,window对象和 document对象。然后是给jquery原型链增加了一个lazyload的方法。下面又增加了四个元素位置判断的jquery方法,最下面$.extend($.expr[":"],{...})又对选择jquery选择器进行了扩展。

众所周知,lazyload的原理,以向下滚动懒加载为例,就是当鼠标向下滚动时图片所在的位置进入可视区域的时候就开始加载图片。

首先看lazyload里面要用的元素位置判断的方法,这些对什么时候开始加载图片至关重要。以$.belowthefold为例:

$.belowthefold = function(element, settings) {
    var fold;

    if (settings.container === undefined || settings.container === window) {
        fold = (window.innerHeight ? window.innerHeight : $window.height()) + $window.scrollTop();
    } else {
        fold = $(settings.container).offset().top + $(settings.container).height();
    }

    return fold <= $(element).offset().top - settings.threshold;
};

对于“fold”这个单词应该如何理解和翻译呢?专门留言咨询下插件作者,作者给的回答是:

The fold in web design is the position on a web page where the majority of browsers viewing the page will begin to scroll.

http://webdesign.about.com/od/layoutglossary/g/bldeffold.htm

以最常见的情况分析,就是整个container是整个窗口的情况,这个时候,fold的值为窗体的高度加上当前鼠标滚动的高度。$window = $(window)settings.threshold是设置提前加载的一个高度。返回值则是是否需要开始加载的布尔值。

再说插件开发的一个常规模式就是设置默认参数,然后和传入进来的参数做合并。lazyload也不例外,它的默认参数就是:

var settings = {
    threshold       : 0,
    failure_limit   : 0,
    event           : "scroll",
    effect          : "show",
    container       : window,
    data_attribute  : "original",
    skip_invisible  : true,
    appear          : null,
    load            : null,
    placeholder     : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC"
};

然后插件初始化的时候合并配置:

if(options) {
    /* Maintain BC for a couple of versions. */
    if (undefined !== options.failurelimit) {
        options.failure_limit = options.failurelimit;
        delete options.failurelimit;
    }
    if (undefined !== options.effectspeed) {
        options.effect_speed = options.effectspeed;
        delete options.effectspeed;
    }

    $.extend(settings, options);
}

$.extentd(settings,options)optionssettings合并。

下面是个给图片绑定加载之后回调做的处理:

$("<img />")
    .bind("load", function() {
        var original = $self.attr("data-" + settings.data_attribute);
        $self.hide();
        if ($self.is("img")) {
            $self.attr("src", original);
        } else {
            $self.css("background-image", "url('" + original + "')");
        }
        $self[settings.effect](settings.effect_speed);

        self.loaded = true;
        //console.log('elements:',elements);
        /* Remove image from array so it is not looped next time. */
        var temp = $.grep(elements, function(element) {
            return !element.loaded;
        });
        //console.log('temp:',temp);
        elements = $(temp);
        //console.log('elements:',elements);

        if (settings.load) {
            var elements_left = elements.length;
            settings.load.call(self, elements_left, settings);
        }
    })
    .attr("src", $self.attr("data-" + settings.data_attribute));

如果上面的三个输出不注释,当我滚动触发一张图片显示之后输出的结果如下:也就是说$.grep()要求传入的是一个Array,但是elements却是一个jquery“数组对象”,虽然是对象,却是众多img一个数组集合,也能传入进去。之后又通过$(temp)还原回jquery数组对象。

还有一个疑惑,为什么是要在appear事件里加上一个动态图片加载呢?

$("<img />")
    .bind("load", function() {
        //对原img的进行修改
    })
    .attr("src", "xxx");

测试发现,虽然可以直接修改图片的src而直接加载图片,但是这样做的意义就是如果原图的data-original数据地址加载失败,则不会进入回调函数。

下面再看appear事件,该事件并无实际操作意义,仅仅是图片加载那一刻的“标志”,然后给所有的$("img")都绑定了该事件:

this.each(function() {
    var self = this;
    var $self = $(self);
    $self.one("appear", function() {
    	//...
    })
})

然后在滚动的时候触发update()函数,而该函数则触发了appear事件($this.trigger("appear"))。

if (0 === settings.event.indexOf("scroll")) {
    $container.bind(settings.event, function() {
        return update();
    });
}

在下面代码中分两种情况,第一种是向下滚动页面的时候,一旦当前元素$.belowthefold(this, settings)不再成立,就触发$this.trigger("appear");第二种情况,是向右滚动的时候,一旦当前元素$.rightoffold(this, settings)不再成立也触发$this.trigger("appear")

function update() {
    var counter = 0;

    elements.each(function() {
        var $this = $(this);
        if (settings.skip_invisible && !$this.is(":visible")) {
            return;
        }
        if ($.abovethetop(this, settings) || $.leftofbegin(this, settings)) {
            /* Nothing. */
        } else if (!$.belowthefold(this, settings) && !$.rightoffold(this, settings)) {
            $this.trigger("appear");
            /* if we found an image we'll load, reset the counter */
            counter = 0;
        } else {
            if (++counter > settings.failure_limit) {
                return false;
            }
        }
    });

}

周末断断续续拆完这个轮子,感觉收获不小啊!赞一个!

评论列表