/*! * An jQuery | zepto plugin for lazy loading images. * author -> jieyou * see https://github.com/jieyou/lazyload * use some tuupola's code https://github.com/tuupola/jquery_lazyload (BSD) * use component's throttle https://github.com/component/throttle (MIT) */ ;(function(factory){ if(typeof define === 'function' && define.amd){ // AMD // you may need to change `define([------>'jquery'<------], factory)` // if you use zepto, change it rely name, such as `define(['zepto'], factory)` define(['jquery'], factory) // define(['zepto'], factory) }else{ // Global factory(window.jQuery || window.Zepto) } })(function($,undefined){ var w = window, $window = $(w), defaultOptions = { threshold : 0, failure_limit : 0, event : 'scroll', effect : 'show', effect_params : null, container : w, data_attribute : 'original', data_srcset_attribute : 'original-srcset', skip_invisible : true, appear : emptyFn, load : emptyFn, vertical_only : false, check_appear_throttle_time : 300, url_rewriter_fn : emptyFn, no_fake_img_loader : false, placeholder_data_img : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC', // for IE6\7 that does not support data image placeholder_real_img : 'http://ditu.baidu.cn/yyfm/lazyload/0.0.1/img/placeholder.png' // todo : 将某些属性用global来配置,而不是每次在$(selector).lazyload({})内配置 }, type // function function emptyFn(){} type = (function(){ var object_prototype_toString = Object.prototype.toString return function(obj){ // todo: compare the speeds of replace string twice or replace a regExp return object_prototype_toString.call(obj).replace('[object ','').replace(']','') } })() function belowthefold($element, options){ var fold if(options._$container == $window){ fold = ('innerHeight' in w ? w.innerHeight : $window.height()) + $window.scrollTop() }else{ fold = options._$container.offset().top + options._$container.height() } return fold <= $element.offset().top - options.threshold } function rightoffold($element, options){ var fold if(options._$container == $window){ // Zepto do not support `$window.scrollLeft()` yet. fold = $window.width() + ($.fn.scrollLeft?$window.scrollLeft():w.pageXOffset) }else{ fold = options._$container.offset().left + options._$container.width() } return fold <= $element.offset().left - options.threshold } function abovethetop($element, options){ var fold if(options._$container == $window){ fold = $window.scrollTop() }else{ fold = options._$container.offset().top } // console.log('abovethetop fold '+ fold) // console.log('abovethetop $element.height() '+ $element.height()) return fold >= $element.offset().top + options.threshold + $element.height() } function leftofbegin($element, options){ var fold if(options._$container == $window){ // Zepto do not support `$window.scrollLeft()` yet. fold = $.fn.scrollLeft?$window.scrollLeft():w.pageXOffset }else{ fold = options._$container.offset().left } return fold >= $element.offset().left + options.threshold + $element.width() } function checkAppear($elements, options){ var counter = 0 $elements.each(function(i,e){ var $element = $elements.eq(i) if(($element.width() <= 0 && $element.height() <= 0) || $element.css('display') === 'none'){ return } function appear(){ $element.trigger('_lazyload_appear') // if we found an image we'll load, reset the counter counter = 0 } // If vertical_only is set to true, only check the vertical to decide appear or not // In most situations, page can only scroll vertically, set vertical_only to true will improve performance if(options.vertical_only){ if(abovethetop($element, options)){ // Nothing. }else if(!belowthefold($element, options)){ appear() }else{ if(++counter > options.failure_limit){ return false } } }else{ if(abovethetop($element, options) || leftofbegin($element, options)){ // Nothing. }else if(!belowthefold($element, options) && !rightoffold($element, options)){ appear() }else{ if(++counter > options.failure_limit){ return false } } } }) } // Remove image from array so it is not looped next time. function getUnloadElements($elements){ return $elements.filter(function(i,e){ return !$elements.eq(i)._lazyload_loadStarted }) } // throttle : https://github.com/component/throttle , MIT License function throttle (func, wait) { var ctx, args, rtn, timeoutID // caching var last = 0 return function throttled () { ctx = this args = arguments var delta = new Date() - last if (!timeoutID) if (delta >= wait) call() else timeoutID = setTimeout(call, wait - delta) return rtn } function call () { timeoutID = 0 last = +new Date() rtn = func.apply(ctx, args) ctx = null args = null } } if(!$.fn.hasOwnProperty('lazyload')){ $.fn.lazyload = function(options){ var $elements = this, isScrollEvent, isScrollTypeEvent, throttleCheckAppear if(!$.isPlainObject(options)){ options = {} } $.each(defaultOptions,function(k,v){ if($.inArray(k,['threshold','failure_limit','check_appear_throttle_time']) != -1){ // these params can be a string if(type(options[k]) == 'String'){ options[k] = parseInt(options[k],10) }else{ options[k] = v } }else if(k == 'container'){ // options.container can be a seletor string \ dom \ jQuery object if(options.hasOwnProperty(k)){ if(options[k] == w || options[k] == document){ options._$container = $window }else{ options._$container = $(options[k]) } }else{ options._$container = $window } delete options.container }else if(defaultOptions.hasOwnProperty(k) && (!options.hasOwnProperty(k) || (type(options[k]) != type(defaultOptions[k])))){ options[k] = v } }) isScrollEvent = options.event == 'scroll' throttleCheckAppear = options.check_appear_throttle_time == 0? checkAppear :throttle(checkAppear,options.check_appear_throttle_time) // isScrollTypeEvent cantains custom scrollEvent . Such as 'scrollstart' & 'scrollstop' // https://github.com/search?utf8=%E2%9C%93&q=scrollstart isScrollTypeEvent = isScrollEvent || options.event == 'scrollstart' || options.event == 'scrollstop' $elements.each(function(i,e){ var element = this, $element = $elements.eq(i), placeholderSrc = $element.attr('src'), originalSrcInAttr = $element.attr('data-'+options.data_attribute), // `data-original` attribute value originalSrc = options.url_rewriter_fn == emptyFn? originalSrcInAttr: options.url_rewriter_fn.call(element,$element,originalSrcInAttr), originalSrcset = $element.attr('data-'+options.data_srcset_attribute), isImg = $element.is('img') if($element._lazyload_loadStarted == true || placeholderSrc == originalSrc){ $element._lazyload_loadStarted = true $elements = getUnloadElements($elements) return } $element._lazyload_loadStarted = false // If element is an img and no src attribute given, use placeholder. if(isImg && !placeholderSrc){ // For browsers that do not support data image. $element.one('error',function(){ // `on` -> `one` : IE6 triggered twice error event sometimes $element.attr('src',options.placeholder_real_img) }).attr('src',options.placeholder_data_img) } // When appear is triggered load original image. $element.one('_lazyload_appear',function(){ var effectParamsIsArray = $.isArray(options.effect_params), effectIsNotImmediacyShow function loadFunc(){ // In most situations, the effect is immediacy show, at this time there is no need to hide element first // Hide this element may cause css reflow, call it as less as possible if(effectIsNotImmediacyShow){ // todo: opacity:0 for fadeIn effect $element.hide() } if(isImg){ // attr srcset first if(originalSrcset){ $element.attr('srcset', originalSrcset) } if(originalSrc){ $element.attr('src', originalSrc) } }else{ $element.css('background-image','url("' + originalSrc + '")') } if(effectIsNotImmediacyShow){ $element[options.effect].apply($element,effectParamsIsArray?options.effect_params:[]) } $elements = getUnloadElements($elements) } if(!$element._lazyload_loadStarted){ effectIsNotImmediacyShow = (options.effect != 'show' && $.fn[options.effect] && (!options.effect_params || (effectParamsIsArray && options.effect_params.length == 0))) if(options.appear != emptyFn){ options.appear.call(element, $element, $elements.length, options) } $element._lazyload_loadStarted = true if(options.no_fake_img_loader || originalSrcset){ if(options.load != emptyFn){ $element.one('load',function(){ options.load.call(element, $element, $elements.length, options) }) } loadFunc() }else{ $('').one('load', function(){ // `on` -> `one` : IE6 triggered twice load event sometimes loadFunc() if(options.load != emptyFn){ options.load.call(element, $element, $elements.length, options) } }).attr('src',originalSrc) } } }) // When wanted event is triggered load original image // by triggering appear. if (!isScrollTypeEvent){ $element.on(options.event, function(){ if (!$element._lazyload_loadStarted){ $element.trigger('_lazyload_appear') } }) } }) // Fire one scroll event per scroll. Not one scroll event per image. if(isScrollTypeEvent){ options._$container.on(options.event, function(){ throttleCheckAppear($elements, options) }) } // Check if something appears when window is resized. // Force initial check if images should appear when window is onload. $window.on('resize load', function(){ throttleCheckAppear($elements, options) }) // Force initial check if images should appear. $(function(){ throttleCheckAppear($elements, options) }) return this } } })