blazy-1.3.1.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. /*!
  2. hey, [be]Lazy.js - v1.3.1 - 2015.02.01
  3. A lazy loading and multi-serving image script
  4. (c) Bjoern Klinggaard - @bklinggaard - http://dinbror.dk/blazy
  5. */
  6. ;(function(root, blazy) {
  7. if (typeof define === 'function' && define.amd) {
  8. // AMD. Register bLazy as an anonymous module
  9. define(blazy);
  10. } else if (typeof exports === 'object') {
  11. // Node. Does not work with strict CommonJS, but
  12. // only CommonJS-like environments that support module.exports,
  13. // like Node.
  14. module.exports = blazy();
  15. } else {
  16. // Browser globals. Register bLazy on window
  17. root.Blazy = blazy();
  18. }
  19. })(this, function () {
  20. 'use strict';
  21. //vars
  22. var source, options, viewport, images, count, isRetina, destroyed;
  23. //throttle vars
  24. var validateT, saveViewportOffsetT;
  25. // constructor
  26. function Blazy(settings) {
  27. //IE7- fallback for missing querySelectorAll support
  28. if (!document.querySelectorAll) {
  29. var s=document.createStyleSheet();
  30. document.querySelectorAll = function(r, c, i, j, a) {
  31. a=document.all, c=[], r = r.replace(/\[for\b/gi, '[htmlFor').split(',');
  32. for (i=r.length; i--;) {
  33. s.addRule(r[i], 'k:v');
  34. for (j=a.length; j--;) a[j].currentStyle.k && c.push(a[j]);
  35. s.removeRule(0);
  36. }
  37. return c;
  38. };
  39. }
  40. //init vars
  41. destroyed = true;
  42. images = [];
  43. viewport = {};
  44. //options
  45. options = settings || {};
  46. options.error = options.error || false;
  47. options.offset = options.offset || 100;
  48. options.success = options.success || false;
  49. options.selector = options.selector || '.b-lazy';
  50. options.separator = options.separator || '|';
  51. options.container = options.container ? document.querySelectorAll(options.container) : false;
  52. options.errorClass = options.errorClass || 'b-error';
  53. options.breakpoints = options.breakpoints || false;
  54. options.successClass = options.successClass || 'b-loaded';
  55. options.src = source = options.src || 'data-src';
  56. isRetina = window.devicePixelRatio > 1;
  57. viewport.top = 0 - options.offset;
  58. viewport.left = 0 - options.offset;
  59. //throttle, ensures that we don't call the functions too often
  60. validateT = throttle(validate, 25);
  61. saveViewportOffsetT = throttle(saveViewportOffset, 50);
  62. saveViewportOffset();
  63. //handle multi-served image src
  64. each(options.breakpoints, function(object){
  65. if(object.width >= window.screen.width) {
  66. source = object.src;
  67. return false;
  68. }
  69. });
  70. // start lazy load
  71. initialize();
  72. }
  73. /* public functions
  74. ************************************/
  75. Blazy.prototype.revalidate = function() {
  76. initialize();
  77. };
  78. Blazy.prototype.load = function(element, force){
  79. if(!isElementLoaded(element)) loadImage(element, force);
  80. };
  81. Blazy.prototype.destroy = function(){
  82. if(options.container){
  83. each(options.container, function(object){
  84. unbindEvent(object, 'scroll', validateT);
  85. });
  86. }
  87. unbindEvent(window, 'scroll', validateT);
  88. unbindEvent(window, 'resize', validateT);
  89. unbindEvent(window, 'resize', saveViewportOffsetT);
  90. count = 0;
  91. images.length = 0;
  92. destroyed = true;
  93. };
  94. /* private helper functions
  95. ************************************/
  96. function initialize(){
  97. // First we create an array of images to lazy load
  98. createImageArray(options.selector);
  99. // Then we bind resize and scroll events if not already binded
  100. if(destroyed) {
  101. destroyed = false;
  102. if(options.container) {
  103. each(options.container, function(object){
  104. bindEvent(object, 'scroll', validateT);
  105. });
  106. }
  107. bindEvent(window, 'resize', saveViewportOffsetT);
  108. bindEvent(window, 'resize', validateT);
  109. bindEvent(window, 'scroll', validateT);
  110. }
  111. // And finally, we start to lazy load. Should bLazy ensure domready?
  112. validate();
  113. }
  114. function validate() {
  115. for(var i = 0; i<count; i++){
  116. var image = images[i];
  117. if(elementInView(image) || isElementLoaded(image)) {
  118. Blazy.prototype.load(image);
  119. images.splice(i, 1);
  120. count--;
  121. i--;
  122. }
  123. }
  124. if(count === 0) {
  125. Blazy.prototype.destroy();
  126. }
  127. }
  128. function loadImage(ele, force){
  129. // if element is visible
  130. if(force || (ele.offsetWidth > 0 && ele.offsetHeight > 0)) {
  131. var dataSrc = ele.getAttribute(source) || ele.getAttribute(options.src); // fallback to default data-src
  132. if(dataSrc) {
  133. var dataSrcSplitted = dataSrc.split(options.separator);
  134. var src = dataSrcSplitted[isRetina && dataSrcSplitted.length > 1 ? 1 : 0];
  135. var img = new Image();
  136. // cleanup markup, remove data source attributes
  137. each(options.breakpoints, function(object){
  138. ele.removeAttribute(object.src);
  139. });
  140. ele.removeAttribute(options.src);
  141. img.onerror = function() {
  142. if(options.error) options.error(ele, "invalid");
  143. ele.className = ele.className + ' ' + options.errorClass;
  144. };
  145. img.onload = function() {
  146. // Is element an image or should we add the src as a background image?
  147. ele.nodeName.toLowerCase() === 'img' ? ele.src = src : ele.style.backgroundImage = 'url("' + src + '")';
  148. ele.className = ele.className + ' ' + options.successClass;
  149. if(options.success) options.success(ele);
  150. };
  151. img.src = src; //preload image
  152. } else {
  153. if(options.error) options.error(ele, "missing");
  154. ele.className = ele.className + ' ' + options.errorClass;
  155. }
  156. }
  157. }
  158. function elementInView(ele) {
  159. var rect = ele.getBoundingClientRect();
  160. return (
  161. // Intersection
  162. rect.right >= viewport.left
  163. && rect.bottom >= viewport.top
  164. && rect.left <= viewport.right
  165. && rect.top <= viewport.bottom
  166. );
  167. }
  168. function isElementLoaded(ele) {
  169. return (' ' + ele.className + ' ').indexOf(' ' + options.successClass + ' ') !== -1;
  170. }
  171. function createImageArray(selector) {
  172. var nodelist = document.querySelectorAll(selector);
  173. count = nodelist.length;
  174. //converting nodelist to array
  175. for(var i = count; i--; images.unshift(nodelist[i])){}
  176. }
  177. function saveViewportOffset(){
  178. viewport.bottom = (window.innerHeight || document.documentElement.clientHeight) + options.offset;
  179. viewport.right = (window.innerWidth || document.documentElement.clientWidth) + options.offset;
  180. }
  181. function bindEvent(ele, type, fn) {
  182. if (ele.attachEvent) {
  183. ele.attachEvent && ele.attachEvent('on' + type, fn);
  184. } else {
  185. ele.addEventListener(type, fn, false);
  186. }
  187. }
  188. function unbindEvent(ele, type, fn) {
  189. if (ele.detachEvent) {
  190. ele.detachEvent && ele.detachEvent('on' + type, fn);
  191. } else {
  192. ele.removeEventListener(type, fn, false);
  193. }
  194. }
  195. function each(object, fn){
  196. if(object && fn) {
  197. var l = object.length;
  198. for(var i = 0; i<l && fn(object[i], i) !== false; i++){}
  199. }
  200. }
  201. function throttle(fn, minDelay) {
  202. var lastCall = 0;
  203. return function() {
  204. var now = +new Date();
  205. if (now - lastCall < minDelay) {
  206. return;
  207. }
  208. lastCall = now;
  209. fn.apply(images, arguments);
  210. };
  211. }
  212. return Blazy;
  213. });