From a3bcef2c6b66f36555d396a1f7bcdc40e99cfa8b Mon Sep 17 00:00:00 2001 From: Eisbehr Date: Tue, 22 Mar 2016 15:59:34 +0100 Subject: [PATCH] 1.6.6 - correctly escape background images (thx @pburke) - use '.on("load", ...)' instead of '.load(...)' (thx @pentie) - 'getItems' now return only not completely loaded elements left (thx @zspitzer) - reformat the whole code --- README.md | 5 +- bower.json | 2 +- jquery.lazy.js | 149 ++++++++++++++++++--------------------------- jquery.lazy.min.js | 4 +- package.json | 2 +- 5 files changed, 66 insertions(+), 96 deletions(-) diff --git a/README.md b/README.md index f18c173..f18c44a 100644 --- a/README.md +++ b/README.md @@ -53,10 +53,10 @@ First of all, you will need a copy of [jQuery](http://jquery.com) to use Lazy su Lazy is available over [cdnjs](http://cdnjs.com) and [jsDelivr](http://jsdelivr.com) CDN and can directly included to every page. ```HTML - + - + ``` #### Self-Hosted @@ -183,6 +183,7 @@ retinaAttribute | *string* | *data-retina* | Name of the image tag attribute, loaderAttribute | *string* | *data-loader* | Name or the element attribute, where the identifier of the customer loader is sored. removeAttribute | *boolean* | *true* | Determine if the attribute should be removed from the element after loading. handledName | *string* | *handled* | Name of the element tag data attribute, to determine if element is already handled. +loadedName | *string* | *loaded* | Name of the element tag data attribute, to determine if element is already loaded. **effect** | | | effect | *string* | *show* | Function name of the effect you want to use to show the loaded images, like `show` or `fadein`. effectTime | *integer* | *0* | Time in milliseconds the effect should use to view the image. diff --git a/bower.json b/bower.json index a40c561..4b160cb 100644 --- a/bower.json +++ b/bower.json @@ -1,7 +1,7 @@ { "name": "jquery-lazy", "description": "Lazy is a fast, feature-rich and lightweight delayed content loading plugin for jQuery. It's designed to speed up page loading times and decrease traffic to your users by only loading the content in view. You can use Lazy in all vertical and horizontal scroll ways. It supports images in 'img' tags and backgrounds, supplied with css like 'background-image', by default or any other content by custom loaders. On images Lazy can set an default image or a placeholder while loading and supports retina displays as well.", - "version": "1.6.5", + "version": "1.6.6", "main": "jquery.lazy.min.js", "license": [ "MIT", diff --git a/jquery.lazy.js b/jquery.lazy.js index 41e1497..edfb368 100644 --- a/jquery.lazy.js +++ b/jquery.lazy.js @@ -1,5 +1,5 @@ /*! - * jQuery Lazy - v1.6.5 + * jQuery Lazy - v1.6.6 * http://jquery.eisbehr.de/lazy/ * * Copyright 2012 - 2016, Daniel 'Eisbehr' Kern @@ -11,13 +11,11 @@ * $("img.lazy").lazy(); */ -;(function($, window, document, undefined) -{ +;(function($, window, document, undefined) { "use strict"; // make lazy a bit more case-insensitive :) - $.fn.Lazy = $.fn.lazy = function(settings) - { + $.fn.Lazy = $.fn.lazy = function(settings) { return new LazyPlugin(this, settings); }; @@ -32,8 +30,7 @@ * @param {object} events * @return void */ - function _executeLazy(instance, configuration, items, events) - { + function _executeLazy(instance, configuration, items, events) { /** * a helper to trigger the 'onFinishedAll' callback after all other events * @access private @@ -68,8 +65,7 @@ * @access private * @return void */ - function _initialize() - { + function _initialize() { // detect actual device pixel ratio // noinspection JSUnresolvedVariable _isRetinaDisplay = window.devicePixelRatio > 1; @@ -81,11 +77,9 @@ if( configuration("delay") >= 0 ) setTimeout(function() { _lazyLoadItems(true); }, configuration("delay")); // if no delay is set or combine usage is active bind events - if( configuration("delay") < 0 || configuration("combined") ) - { + if( configuration("delay") < 0 || configuration("combined") ) { // create unique event function - events.e = _throttle(configuration("throttle"), function(event) - { + events.e = _throttle(configuration("throttle"), function(event) { // reset detected window size on resize event if( event.type === "resize" ) _actualWidth = _actualHeight = -1; @@ -95,16 +89,17 @@ }); // create function to add new items to instance - events.a = function(additionalItems) - { + events.a = function(additionalItems) { _prepareItems(additionalItems); items.push.apply(items, additionalItems); }; // create function to get all instance items left - events.g = function() - { - return items; + events.g = function() { + // filter loaded items before return in case internal filter was not running until now + return (items = $(items).filter(function() { + return !$(this).data(configuration("loadedName")); + })); }; // load initial items @@ -121,11 +116,9 @@ * @param {Array|object|jQuery} items * @return void */ - function _prepareItems(items) - { + function _prepareItems(items) { // filter items and only add those who not handled yet and got needed attributes available - items = $(items).filter(function() - { + items = $(items).filter(function() { return !$(this).data(configuration("handledName")) && ($(this).attr(configuration("attribute")) || $(this).attr(configuration("loaderAttribute"))); }) @@ -134,8 +127,7 @@ // set default image and/or placeholder to elements if configured if( configuration("defaultImage") || configuration("placeholder") ) - for( var i = 0; i < items.length; i++ ) - { + for( var i = 0; i < items.length; i++ ) { var element = $(items[i]), tag = items[i].tagName.toLowerCase(), propertyName = "background-image"; @@ -156,11 +148,9 @@ * @param {boolean} [allItems] * @return void */ - function _lazyLoadItems(allItems) - { + function _lazyLoadItems(allItems) { // skip if no items where left - if( !items.length ) - { + if( !items.length ) { // destroy instance if option is enabled if( configuration("autoDestroy") ) // noinspection JSUnresolvedFunction @@ -177,11 +167,9 @@ // loop all available items for( var i = 0; i < items.length; i++ ) - (function(item) - { + (function(item) { // item is at least in loadable area - if( _isInLoadableArea(item) || allItems ) - { + if( _isInLoadableArea(item) || allItems ) { var element = $(item), tag = item.tagName.toLowerCase(), attribute = element.attr(configuration("attribute")), @@ -200,7 +188,7 @@ // or custom loader is available (customLoader = element.attr(configuration("loaderAttribute"))) )) { - // mark element always as handled as this point to prevent double loading + // mark element always as handled as this point to prevent double handling loadTriggered = true; element.data(configuration("handledName"), true); @@ -212,9 +200,8 @@ // when something was loaded remove them from remaining items if( loadTriggered ) - items = $(items).filter(function() - { - return !$(this).data(configuration("handledName")); + items = $(items).filter(function() { + return !$(this).data(configuration("loadedName")); }); } @@ -227,14 +214,12 @@ * @param {function} [customLoader] * @return void */ - function _handleItem(element, tag, imageBase, customLoader) - { + function _handleItem(element, tag, imageBase, customLoader) { // increment count of items waiting for after load ++_awaitingAfterLoad; // extended error callback for correct 'onFinishedAll' handling - var errorCallback = function() - { + var errorCallback = function() { _triggerCallback("onError", element); _reduceAwaiting(); }; @@ -243,18 +228,19 @@ _triggerCallback("beforeLoad", element); // handle custom loader - if( customLoader ) - { + if( customLoader ) { // bind error event to trigger callback and reduce waiting amount element.off("error").one("error", errorCallback); // bind after load callback to image - element.one("load", function() - { + element.one("load", function() { // remove attribute from element if( configuration("removeAttribute") ) element.removeAttr(configuration("loaderAttribute")); + // mark element as loaded + element.data(configuration("loadedName"), true); + // call after load event _triggerCallback("afterLoad", element); @@ -263,16 +249,14 @@ }); // trigger custom loader - if( !_triggerCallback(customLoader, element, function(response) - { + if( !_triggerCallback(customLoader, element, function(response) { if( response ) element.load(); else element.error(); })) element.error(); } // handle images - else - { + else { // create image object var imageObj = $(new Image()); @@ -280,8 +264,7 @@ imageObj.one("error", errorCallback); // bind after load callback to image - imageObj.one("load", function() - { + imageObj.one("load", function() { // remove element from view element.hide(); @@ -296,6 +279,9 @@ if( configuration("removeAttribute") ) element.removeAttr(configuration("attribute") + " " + configuration("retinaAttribute")); + // mark element as loaded + element.data(configuration("loadedName"), true); + // call after load event _triggerCallback("afterLoad", element); @@ -320,8 +306,7 @@ * @param {object} element * @return {boolean} */ - function _isInLoadableArea(element) - { + function _isInLoadableArea(element) { var elementBound = element.getBoundingClientRect(), direction = configuration("scrollDirection"), threshold = configuration("threshold"), @@ -333,7 +318,7 @@ ((_getActualWidth() + threshold) > elementBound.left) && // check if element is even in loadable area from right (-threshold < elementBound.right); - + if( direction == "vertical" ) return vertical; else if( direction == "horizontal" ) return horizontal; @@ -345,8 +330,7 @@ * @access private * @return {number} */ - function _getActualWidth() - { + function _getActualWidth() { return _actualWidth >= 0 ? _actualWidth : (_actualWidth = $(window).width()); } @@ -355,8 +339,7 @@ * @access private * @return {number} */ - function _getActualHeight() - { + function _getActualHeight() { return _actualHeight >= 0 ? _actualHeight : (_actualHeight = $(window).height()); } @@ -367,16 +350,13 @@ * @param {function} callback * @return {function} */ - function _throttle(delay, callback) - { + function _throttle(delay, callback) { var timeout, lastExecute = 0; - return function(event, ignoreThrottle) - { + return function(event, ignoreThrottle) { var elapsed = +new Date() - lastExecute; - function run() - { + function run() { lastExecute = +new Date(); callback.call(instance, event); } @@ -393,8 +373,7 @@ * @access private * @return void */ - function _reduceAwaiting() - { + function _reduceAwaiting() { --_awaitingAfterLoad; // if no items were left trigger finished event @@ -409,10 +388,8 @@ * @param {*} [args] * @return {boolean} */ - function _triggerCallback(callback, element, args) - { - if( (callback = configuration(callback)) ) - { + function _triggerCallback(callback, element, args) { + if( (callback = configuration(callback)) ) { // jQuery's internal '$(arguments).slice(1)' are causing problems at least on old iPads // below is shorthand of 'Array.prototype.slice.call(arguments, 1)' callback.apply(instance, [].slice.call(arguments, 1)); @@ -423,13 +400,12 @@ } // set up lazy - (function() - { + (function() { // if event driven don't wait for page loading if( configuration("bind") == "event" ) _initialize(); // otherwise load initial items and start lazy after page load - else $(window).load(_initialize); + else $(window).on("load." + instance.name, _initialize); })(); } @@ -441,8 +417,7 @@ * @param {object} settings * @return {object|LazyPlugin} */ - function LazyPlugin(elements, settings) - { + function LazyPlugin(elements, settings) { /** * this lazy plugin instance * @access private @@ -475,8 +450,7 @@ * @param {*} [value] * @return {LazyPlugin|*} */ - _instance.config = function(entryName, value) - { + _instance.config = function(entryName, value) { if( value === undefined ) return _config[entryName]; else @@ -492,8 +466,7 @@ * @param {Array|object|string} items * @return {LazyPlugin} */ - _instance.addItems = function(items) - { + _instance.addItems = function(items) { if( _events.a ) _events.a($.type(items) === "string" ? $(items) : items); @@ -506,8 +479,7 @@ * @access public * @returns {object} */ - _instance.getItems = function() - { + _instance.getItems = function() { return _events.g ? _events.g() : {}; }; @@ -520,8 +492,7 @@ * @param {boolean} [useThrottle] * @return {LazyPlugin} */ - _instance.update = function(useThrottle) - { + _instance.update = function(useThrottle) { if( _events.e ) _events.e({}, !useThrottle); @@ -536,8 +507,7 @@ * @type {function} * @return {LazyPlugin} */ - _instance.loadAll = function() - { + _instance.loadAll = function() { if( _events.e ) _events.e({all: true}, true); @@ -551,11 +521,11 @@ * @type {function} * @return undefined */ - _instance.destroy = function () - { + _instance.destroy = function () { // unbind instance generated events // noinspection JSUnresolvedFunction $(_instance.config("appendScroll")).off("." + _instance.name, _events.e); + $(window).off("." + _instance.name); // clear events _events = {}; @@ -571,8 +541,7 @@ } // use jquery to extend class prototype without conflicts - $.extend(LazyPlugin.prototype, - { + $.extend(LazyPlugin.prototype, { /** * internal name used for bindings and namespaces * @access public @@ -585,8 +554,7 @@ * @access public * @type {object} */ - configuration: - { + configuration: { // general chainable : true, autoDestroy : true, @@ -607,6 +575,7 @@ loaderAttribute : "data-loader", removeAttribute : true, handledName : "handled", + loadedName : "loaded", // effect effect : "show", diff --git a/jquery.lazy.min.js b/jquery.lazy.min.js index ae84c23..f58aa53 100644 --- a/jquery.lazy.min.js +++ b/jquery.lazy.min.js @@ -1,2 +1,2 @@ -/*! jQuery Lazy 1.6.5 - MIT & GPL-2.0 license - Copyright 2012-2016 Daniel 'Eisbehr' Kern */ -!function(t,e,r,n){"use strict";function a(r,n,a,o){function i(){p=e.devicePixelRatio>1,l(a),n("delay")>=0&&setTimeout(function(){u(!0)},n("delay")),(n("delay")<0||n("combined"))&&(o.e=m(n("throttle"),function(t){"resize"===t.type&&(b=v=-1),u(t.all)}),o.a=function(t){l(t),a.push.apply(a,t)},o.g=function(){return a},u(),t(n("appendScroll")).on("scroll."+r.name+" resize."+r.name,o.e))}function l(e){if(e=t(e).filter(function(){return!t(this).data(n("handledName"))&&(t(this).attr(n("attribute"))||t(this).attr(n("loaderAttribute")))}).data("plugin_"+r.name,r),n("defaultImage")||n("placeholder"))for(var a=0;ae.top&&-ae.left&&-a=0?b:b=t(e).width()}function s(){return v>=0?v:v=t(e).height()}function m(t,e){var a,o=0;return function(i,l){function u(){o=+new Date,e.call(r,i)}var c=+new Date-o;a&&clearTimeout(a),c>t||!n("enableThrottle")||l?u():a=setTimeout(u,t-c)}}function A(){--h,a.size()||h||g("onFinishedAll")}function g(t,e,a){return(t=n(t))?(t.apply(r,[].slice.call(arguments,1)),!0):!1}var h=0,b=-1,v=-1,p=!1;!function(){"event"==n("bind")?i():t(e).load(i)}()}function o(e,r){var o=this,i=t.extend({},o.configuration,r),l={};return o.config=function(t,e){return e===n?i[t]:(i[t]=e,o)},o.addItems=function(e){return l.a&&l.a("string"===t.type(e)?t(e):e),o},o.getItems=function(){return l.g?l.g():{}},o.update=function(t){return l.e&&l.e({},!t),o},o.loadAll=function(){return l.e&&l.e({all:!0},!0),o},o.destroy=function(){return t(o.config("appendScroll")).off("."+o.name,l.e),l={},n},a(o,o.config,e,l),o.config("chainable")?e:o}t.fn.Lazy=t.fn.lazy=function(t){return new o(this,t)},t.extend(o.prototype,{name:"lazy",configuration:{chainable:!0,autoDestroy:!0,bind:"load",threshold:500,visibleOnly:!1,appendScroll:e,scrollDirection:"both",imageBase:null,defaultImage:"data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==",placeholder:null,delay:-1,combined:!1,attribute:"data-src",retinaAttribute:"data-retina",loaderAttribute:"data-loader",removeAttribute:!0,handledName:"handled",effect:"show",effectTime:0,enableThrottle:!0,throttle:250,beforeLoad:null,afterLoad:null,onError:null,onFinishedAll:null}})}(jQuery,window,document); \ No newline at end of file +/*! jQuery Lazy 1.6.6 - MIT & GPL-2.0 license - Copyright 2012-2016 Daniel 'Eisbehr' Kern */ +!function(e,t,a,r){"use strict";function n(a,r,n,o){function i(){p=t.devicePixelRatio>1,l(n),r("delay")>=0&&setTimeout(function(){u(!0)},r("delay")),(r("delay")<0||r("combined"))&&(o.e=m(r("throttle"),function(e){"resize"===e.type&&(b=v=-1),u(e.all)}),o.a=function(e){l(e),n.push.apply(n,e)},o.g=function(){return n=e(n).filter(function(){return!e(this).data(r("loadedName"))})},u(),e(r("appendScroll")).on("scroll."+a.name+" resize."+a.name,o.e))}function l(t){if(t=e(t).filter(function(){return!e(this).data(r("handledName"))&&(e(this).attr(r("attribute"))||e(this).attr(r("loaderAttribute")))}).data("plugin_"+a.name,a),r("defaultImage")||r("placeholder"))for(var n=0;nt.top&&-nt.left&&-n=0?b:b=e(t).width()}function s(){return v>=0?v:v=e(t).height()}function m(e,t){var n,o=0;return function(i,l){function u(){o=+new Date,t.call(a,i)}var d=+new Date-o;n&&clearTimeout(n),d>e||!r("enableThrottle")||l?u():n=setTimeout(u,e-d)}}function A(){--h,n.size()||h||g("onFinishedAll")}function g(e,t,n){return(e=r(e))?(e.apply(a,[].slice.call(arguments,1)),!0):!1}var h=0,b=-1,v=-1,p=!1;!function(){"event"==r("bind")?i():e(t).on("load."+a.name,i)}()}function o(a,o){var i=this,l=e.extend({},i.configuration,o),u={};return i.config=function(e,t){return t===r?l[e]:(l[e]=t,i)},i.addItems=function(t){return u.a&&u.a("string"===e.type(t)?e(t):t),i},i.getItems=function(){return u.g?u.g():{}},i.update=function(e){return u.e&&u.e({},!e),i},i.loadAll=function(){return u.e&&u.e({all:!0},!0),i},i.destroy=function(){return e(i.config("appendScroll")).off("."+i.name,u.e),e(t).off("."+i.name),u={},r},n(i,i.config,a,u),i.config("chainable")?a:i}e.fn.Lazy=e.fn.lazy=function(e){return new o(this,e)},e.extend(o.prototype,{name:"lazy",configuration:{chainable:!0,autoDestroy:!0,bind:"load",threshold:500,visibleOnly:!1,appendScroll:t,scrollDirection:"both",imageBase:null,defaultImage:"data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==",placeholder:null,delay:-1,combined:!1,attribute:"data-src",retinaAttribute:"data-retina",loaderAttribute:"data-loader",removeAttribute:!0,handledName:"handled",loadedName:"loaded",effect:"show",effectTime:0,enableThrottle:!0,throttle:250,beforeLoad:null,afterLoad:null,onError:null,onFinishedAll:null}})}(jQuery,window,document); \ No newline at end of file diff --git a/package.json b/package.json index a4ea114..9f19ff3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "jquery-lazy", "title": "jQuery Lazy - Delayed Content, Image and Background Loader", - "version": "1.6.5", + "version": "1.6.6", "description": "Lazy is a fast, feature-rich and lightweight delayed content loading plugin for jQuery. It's designed to speed up page loading times and decrease traffic to your users by only loading the content in view.", "homepage": "http://jquery.eisbehr.de/lazy/", "bugs": "http://github.com/eisbehr-/jquery.lazy/issues",