图片延迟加载demo

Talk is cheap,show me the Code~先上代码,再说心路

/**
 * lazyload demo
 * 
 */
(function($, window, document, undefined) {
    $.fn.Lazyload = function(opts) {
        var elements = this,
            timer = false,
            defaults = {
                data_attribute: 'data-src',
                threshold: 0
            },
            options = $.extend({}, defaults, opts);
        //最初进入页面从所有元素中筛选出在视区内的元素执行任务
        bindEvents();
        //滚动事件停止后视区内的元素执行任务
        $(window).on("scroll", function() {
            if (timer) { clearTimeout(timer); }
            timer = setTimeout(bindEvents, 1000);
        })

        function bindEvents() {
            elements.each(function() {
                var me = this;
                me.loaded = false; //将所有元素标记为  未执行任务,执行过appear()的元素该属性就会标记为true
                if (checkIsInView(me)) {
                    appear(me);
                }
            });
            //将元素列表的更新放到一屏结束后统一更新一次
            var temp = $.grep(elements, function(element) {
                return (!element.loaded);
            })
            elements = $(temp);

        };

        function checkIsInView(element) {
            var isAboveTheView,
                isBelowTheView,
                $window = $(window);

            isAboveTheView = ($(element).offset().top + $(element).height() + options.threshold < $window.scrollTop()) ? true : false;
            isBelowTheView = ($(element).offset().top - options.threshold > $window.scrollTop() + $window.height()) ? true : false;
            if (!isBelowTheView && !isAboveTheView) {
                return true;
            } else {
                return false;
            }
        };

        function appear(element) {
            var data_src = $(element).attr(options.data_attribute);
            $(element).attr("src", data_src);
            element.loaded = true;
        };

    }
})(window.jQuery, window, document);

心路历程&查漏补缺

1、模式的转变

最开始使用了构造函数加原型的混合模式,但是后来发现这样写很有问题(当然很有可能是我掌握不精所致),直接�把这个完整的方法赋给每个元素,不容易将执行完任务的元素从待执行任务的元素列表中移除,所以后来改成了单例模式,然后这个就越看越像是jquery.lazyload.js的极简版。
那么总结下思路的转变过程:

滚动停止后对所有元素都去判断是否在视区,执行相应任务——改为——滚动停止后从待执行任务的元素列表中搜索留在视区内的元素,显示。

另外,在模式转变的过程中函数方法的定义方式也要发生改变,这使得我对原型函数中的对象方法有了进一步的认识。我们都知道在js中一切都是对象,对象方法和对象属性没什么不同,只是前者的值是函数而已,所以原型函数中的对象方法看起来也都是键值对的样子。想访问该属性,当做字符串访问,想调用该方法加(),如:

var person = {
    firstName: "John",
    lastName : "Doe",
    id       : 5566,
    fullName : function() {
       return this.firstName + " " + this.lastName;
    }
};
console.log(person.fullName);//function() { return this.firstName + " " + this.lastName;}
console.log(person.fullName());//John Doe

2、更新元素列表这项工作放到哪儿执行更好?

每个元素的图片被替换之后就立即更新列表,还是这一屏的元素的图片都替换完了之后再统一更新一次?这两者哪个耗时更短?前者是筛选次数执行的多,后者是查找的次数执行的多,个人从理论上分析,筛选是要更耗时的,因为一次筛选过程要查找然后更新新数组(不管其内部用的是数组还是指针都要多几步的,那么对于数组元素的删除、过滤其内部是如何实现的还有待考究)。所以我是在一屏的图片被替换后更新一次元素列表。

这是更新数组的代码:

      var temp=$.grep(elements,function(element){
            return (!element.loaded);//最开始遍历元素的时候给每个元素设置一个属性loaded作为标志位
       })
       elements=temp;

3、添加限制条件:滚动停止后才执行替换图片的任务

这一知识也是听师父介绍的,这个在淘宝无线端有应用。这儿利用setTimeout来判断滚动事件停止,滚动过程中不去加载。(像移动端有区分touchStart,touchEnd事件就不用这么麻烦了)

 $(window).on("scroll", function() {
     if (timer) { clearTimeout(timer); }
     timer = setTimeout(bindEvents, 1000);
 })

最开始的时候把timer定义在了scroll事件里,然后就理所当然的发生里一些不可思议的事情。。。真是粗心大意害死人。

4、获取到文档顶部距离

jquery中获取元素到文档顶部的距离:$().offset().top
当前视口到文档顶部的距离: scrollTop()
原生javascript中只能获取相对父元素的left,top,那么是如何获得距离文档顶部的距离的?
一层层递归到父元素为body

function getOffsetTop(element){
  var actualTop = element.offsetTop;
  var current = element.offsetParent;
  while (current !== null){
    actualTop += current.offsetTop;
    current = current.offsetParent;
  }
  return actualTop;
}

你可能感兴趣的:(图片延迟加载demo)