shautvast.github.io/assets/js/lazyload.js
2023-08-24 21:05:05 +02:00

258 lines
7.1 KiB
JavaScript

/*! lazyload - v2.1.1 - 2018-04-01
* https://github.com/13twelve/lazyload
* Copyright (c) 2018
* License: MIT
* Author: Mike Byrne @13twelve https://github.com/13twelve
*/
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define([], factory);
} else if (typeof exports === 'object') {
// Node. Does not work with strict CommonJS, but
// only CommonJS-like environments that support module.exports,
// like Node.
module.exports = factory();
} else {
// Browser globals (root is window)
root.lazyLoad = factory();
}
}(this, function () {
var options = {
pageUpdatedEventName: 'page:updated', // how your app tells the rest of the app an update happened
elements: 'img[data-src], img[data-srcset], source[data-srcset], iframe[data-src], video[data-src], [data-lazyload]', // maybe you just want images?
rootMargin: '0px', // IntersectionObserver option
threshold: 0, // IntersectionObserver option
maxFrameCount: 10, // 60fps / 10 = 6 times a second
};
// set up
var frameLoop;
var frameCount;
var els = [];
var elsLength;
var observer;
var checkType;
/**
* Converts HTML collections to an array
* @private
* @param {Array} array to convert
* a loop will work in more browsers than the slice method
*/
function _htmlCollectionToArray(collection) {
var a = [];
var i = 0;
for (a = [], i = collection.length; i;) {
a[--i] = collection[i];
}
return a;
}
/**
* Checks if an element is in the viewport
* @private
* @param {Node} element to check.
* @returns {Boolean} true/false.
*/
function _elInViewport(el) {
el = (el.tagName === 'SOURCE') ? el.parentNode : el;
var rect = el.getBoundingClientRect();
return rect.bottom > 0 && rect.right > 0 && rect.left < (window.innerWidth || document.documentElement.clientWidth) && rect.top < (window.innerHeight || document.documentElement.clientHeight);
}
/**
* Removes data- attributes
* @private
* @param {Node} element to update
*/
function _removeDataAttrs(el) {
el.removeAttribute('data-src');
el.removeAttribute('data-srcset');
el.removeAttribute('data-lazyload');
}
/**
* On loaded, removes event listener, removes data- attributes
* @private
*/
function _loaded() {
this.removeEventListener('load', _loaded);
_removeDataAttrs(this);
}
/**
* Update an element
* @private
* @param {Node} element to update
*/
function _updateEl(el) {
var srcset = el.getAttribute('data-srcset');
var src = el.getAttribute('data-src');
var dlazyload = el.getAttribute('data-lazyload') !== null;
//
if (srcset) {
// if source set, update and try picturefill
el.setAttribute('srcset', srcset);
if (window.picturefill) {
window.picturefill({
elements: [el]
});
}
}
if (src) {
// if source set, update
el.src = src;
}
if (dlazyload) {
el.setAttribute('data-lazyloaded','');
el.removeEventListener('load', _loaded);
_removeDataAttrs(el);
}
}
/**
* The callback from the IntersectionObserver
* @private
* @entries {Nodes} elements being observed by the IntersectionObserver
*/
function _intersection(entries) {
// Disconnect if we've already loaded all of the images
if (elsLength === 0) {
observer.disconnect();
}
// Loop through the entries
for (var i = 0; i < entries.length; i++) {
var entry = entries[i];
// Are we in viewport?
if (entry.intersectionRatio > 0) {
elsLength--;
// Stop watching this and load the image
observer.unobserve(entry.target);
entry.target.addEventListener('load', _loaded, false);
_updateEl(entry.target);
}
}
}
/**
* Loops images, checks if in viewport, updates src/src-set
* @private
*/
function _setSrcs() {
var i;
// browser capability check
if (checkType === 'really-old') {
elsLength = els.length;
for (i = 0; i < elsLength; i++) {
if (els[i]) {
_updateEl(els[i]);
_removeDataAttrs(els[i]);
}
}
els = [];
} else if (checkType === 'old') {
// debounce checking
if (frameCount === options.maxFrameCount) {
// update cache of this for the loop
elsLength = els.length;
for (i = 0; i < elsLength; i++) {
// check if this array item exists, hasn't been loaded already and is in the viewport
if (els[i] && els[i].lazyloaded === undefined && _elInViewport(els[i])) {
// cache this array item
var thisEl = els[i];
// set this array item to be undefined to be cleaned up later
els[i] = undefined;
// give this element a property to stop us running twice on one thing
thisEl.lazyloaded = true;
// add an event listener to remove data- attributes on load
thisEl.addEventListener('load', _loaded, false);
// update
_updateEl(thisEl);
}
}
// clean up array
for (i = 0; i < elsLength; i++) {
if (els[i] === undefined) {
els.splice(i, 1);
}
}
// reset var to decide if to continue running
elsLength = els.length;
// will shortly be set to 0 to start counting
frameCount = -1;
}
// run again? kill if not
if (elsLength > 0) {
frameCount++;
frameLoop = window.requestAnimationFrame(_setSrcs);
}
} else if (checkType === 'new') {
observer = new IntersectionObserver(_intersection, {
rootMargin: options.rootMargin,
threshold: options.threshold,
});
elsLength = els.length;
for (i = 0; i < elsLength; i++) {
if (els[i] && els[i].lazyloaded === undefined) {
observer.observe(els[i]);
}
}
}
}
/**
* Gets the show on the road
* @private
*/
function _init() {
// kill any old loops if there are any
if (checkType === 'old') {
try {
cancelAnimationFrame(frameLoop);
} catch(err) {}
} else if (checkType === 'new') {
try {
observer.disconnect();
} catch(err) {}
}
// grab elements to lazy load
els = _htmlCollectionToArray(document.querySelectorAll(options.elements));
elsLength = els.length;
frameCount = options.maxFrameCount;
// go go go
_setSrcs();
}
/**
* GO GO GO
* @public
* @param {object} options (see readme)
*/
var lazyLoad = function(opts) {
for(var item in opts) {
if(opts.hasOwnProperty(item)) {
options[item] = opts[item];
}
}
if(!('addEventListener' in window) || !window.requestAnimationFrame || typeof document.body.getBoundingClientRect === undefined) {
checkType = 'really-old';
} else if ('IntersectionObserver' in window) {
checkType = 'new';
} else {
checkType = 'old';
}
_init();
if (options.pageUpdatedEventName) {
document.addEventListener(options.pageUpdatedEventName, _init, true);
}
};
return lazyLoad;
}));
lazyLoad();