vanilla-lazyload
Version:
A fast, lightweight script to load images as they enter the viewport. SEO friendly, it supports responsive images (both srcset + sizes and picture) and progressive JPEG
178 lines (153 loc) • 4.36 kB
JavaScript
import getDefaultSettings from "./lazyload.defaults";
import { purgeProcessedElements } from "./lazyload.purge";
import isInsideViewport from "./lazyload.viewport";
import autoInitialize from "./lazyload.autoInitialize";
import { addClass } from "./lazyload.class";
import { isBot, runningOnBrowser } from "./lazyload.environment";
import { revealElement } from "./lazyload.reveal";
import { removeFromArray } from "./lazyload.array";
import { callbackIfSet } from "./lazyload.callback";
/*
* Constructor
*/
const LazyLoad = function(instanceSettings) {
this._settings = Object.assign({}, getDefaultSettings(), instanceSettings);
this._loadingCount = 0;
this._queryOriginNode =
this._settings.container === window
? document
: this._settings.container;
this._previousLoopTime = 0;
this._loopTimeout = null;
this._boundHandleScroll = this.handleScroll.bind(this);
this._isFirstLoop = true;
window.addEventListener("resize", this._boundHandleScroll);
this.update();
};
LazyLoad.prototype = {
_loopThroughElements: function(forceDownload) {
const settings = this._settings,
elements = this._elements,
elementsLength = !elements ? 0 : elements.length;
let i,
processedIndexes = [],
isFirstLoop = this._isFirstLoop;
if (isFirstLoop) {
this._isFirstLoop = false;
}
if (elementsLength === 0) {
this._stopScrollHandler();
return;
}
for (i = 0; i < elementsLength; i++) {
let element = elements[i];
/* If must skip_invisible and element is invisible, skip it */
if (settings.skip_invisible && element.offsetParent === null) {
continue;
}
if (
forceDownload ||
isInsideViewport(
element,
settings.container,
settings.threshold
)
) {
if (isFirstLoop) {
addClass(element, settings.class_initial);
}
this.load(element);
processedIndexes.push(i);
}
}
// Removing processed elements from this._elements.
removeFromArray(elements, processedIndexes);
},
_startScrollHandler: function() {
if (!this._isHandlingScroll) {
this._isHandlingScroll = true;
this._settings.container.addEventListener(
"scroll",
this._boundHandleScroll
);
}
},
_stopScrollHandler: function() {
if (this._isHandlingScroll) {
this._isHandlingScroll = false;
this._settings.container.removeEventListener(
"scroll",
this._boundHandleScroll
);
}
},
_updateLoadingCount: function(plusMinus) {
this._loadingCount += plusMinus;
if (this._elements.length === 0 && this._loadingCount === 0) {
callbackIfSet(this._settings.callback_finish);
}
},
handleScroll: function() {
const throttle = this._settings.throttle;
if (throttle !== 0) {
let now = Date.now();
let remainingTime = throttle - (now - this._previousLoopTime);
if (remainingTime <= 0 || remainingTime > throttle) {
if (this._loopTimeout) {
clearTimeout(this._loopTimeout);
this._loopTimeout = null;
}
this._previousLoopTime = now;
this._loopThroughElements();
} else if (!this._loopTimeout) {
this._loopTimeout = setTimeout(
function() {
this._previousLoopTime = Date.now();
this._loopTimeout = null;
this._loopThroughElements();
}.bind(this),
remainingTime
);
}
} else {
this._loopThroughElements();
}
},
loadAll: function() {
this._loopThroughElements(true);
},
update: function(elements) {
const settings = this._settings;
const nodeSet =
elements ||
this._queryOriginNode.querySelectorAll(settings.elements_selector);
this._elements = purgeProcessedElements(
Array.prototype.slice.call(nodeSet) // NOTE: nodeset to array for IE compatibility
);
if (isBot) {
this.loadAll();
return;
}
this._loopThroughElements();
this._startScrollHandler();
},
destroy: function() {
window.removeEventListener("resize", this._boundHandleScroll);
if (this._loopTimeout) {
clearTimeout(this._loopTimeout);
this._loopTimeout = null;
}
this._stopScrollHandler();
this._elements = null;
this._queryOriginNode = null;
this._settings = null;
},
load: function(element, force) {
revealElement(element, this, force);
}
};
/* Automatic instances creation if required (useful for async script loading) */
if (runningOnBrowser) {
autoInitialize(LazyLoad, window.lazyLoadOptions);
}
export default LazyLoad;