vanilla-lazyload
Advanced tools
Comparing version 10.9.0 to 10.10.0
@@ -5,2 +5,6 @@ # CHANGELOG | ||
#### 10.10.0 | ||
Added a public `load` method to force loading any element. | ||
#### 10.9.0 | ||
@@ -112,2 +116,11 @@ | ||
#### 8.11.0 | ||
Added a public `load` method to force loading any element. | ||
#### 8.10.0 | ||
Added the ability to lazily set the `sizes` attribute via a `data-sizes` attribute. | ||
See the [README](README.md) file for more information. | ||
#### 8.9.0 | ||
@@ -114,0 +127,0 @@ |
@@ -28,15 +28,21 @@ (function (global, factory) { | ||
const dataPrefix = "data-"; | ||
const processedDataName = "was-processed"; | ||
const processedDataValue = "true"; | ||
const getData = (element, attribute) => { | ||
return element.getAttribute(dataPrefix + attribute); | ||
return element.getAttribute(dataPrefix + attribute); | ||
}; | ||
const setData = (element, attribute, value) => { | ||
return element.setAttribute(dataPrefix + attribute, value); | ||
return element.setAttribute(dataPrefix + attribute, value); | ||
}; | ||
function purgeElements (elements) { | ||
return elements.filter((element) => { | ||
return !getData(element, "was-processed"); | ||
}); | ||
const setWasProcessed = element => | ||
setData(element, processedDataName, processedDataValue); | ||
const getWasProcessed = element => | ||
getData(element, processedDataName) === processedDataValue; | ||
function purgeElements(elements) { | ||
return elements.filter(element => !getWasProcessed(element)); | ||
} | ||
@@ -104,27 +110,27 @@ | ||
const srcDataValue = getData(element, srcDataName); | ||
const tagName = element.tagName; | ||
if (tagName === "IMG") { | ||
const parent = element.parentNode; | ||
if (parent && parent.tagName === "PICTURE") { | ||
setSourcesInChildren(parent, "srcset", srcsetDataName); | ||
switch (element.tagName) { | ||
case "IMG": { | ||
const parent = element.parentNode; | ||
if (parent && parent.tagName === "PICTURE") { | ||
setSourcesInChildren(parent, "srcset", srcsetDataName); | ||
} | ||
const sizesDataValue = getData(element, sizesDataName); | ||
setAttributeIfNotNullOrEmpty(element, "sizes", sizesDataValue); | ||
const srcsetDataValue = getData(element, srcsetDataName); | ||
setAttributeIfNotNullOrEmpty(element, "srcset", srcsetDataValue); | ||
setAttributeIfNotNullOrEmpty(element, "src", srcDataValue); | ||
break; | ||
} | ||
const sizesDataValue = getData(element, sizesDataName); | ||
setAttributeIfNotNullOrEmpty(element, "sizes", sizesDataValue); | ||
const srcsetDataValue = getData(element, srcsetDataName); | ||
setAttributeIfNotNullOrEmpty(element, "srcset", srcsetDataValue); | ||
setAttributeIfNotNullOrEmpty(element, "src", srcDataValue); | ||
return; | ||
case "IFRAME": | ||
setAttributeIfNotNullOrEmpty(element, "src", srcDataValue); | ||
break; | ||
case "VIDEO": | ||
setSourcesInChildren(element, "src", srcDataName); | ||
setAttributeIfNotNullOrEmpty(element, "src", srcDataValue); | ||
break; | ||
default: | ||
if (srcDataValue) { | ||
element.style.backgroundImage = `url("${srcDataValue}")`; | ||
} | ||
} | ||
if (tagName === "IFRAME") { | ||
setAttributeIfNotNullOrEmpty(element, "src", srcDataValue); | ||
return; | ||
} | ||
if (tagName === "VIDEO") { | ||
setSourcesInChildren(element, "src", srcDataName); | ||
setAttributeIfNotNullOrEmpty(element, "src", srcDataValue); | ||
return; | ||
} | ||
if (srcDataValue) { | ||
element.style.backgroundImage = `url("${srcDataValue}")`; | ||
} | ||
}; | ||
@@ -139,21 +145,24 @@ | ||
const addClass = (element, className) => { | ||
if (supportsClassList) { | ||
element.classList.add(className); | ||
return; | ||
} | ||
element.className += (element.className ? " " : "") + className; | ||
if (supportsClassList) { | ||
element.classList.add(className); | ||
return; | ||
} | ||
element.className += (element.className ? " " : "") + className; | ||
}; | ||
const removeClass = (element, className) => { | ||
if (supportsClassList) { | ||
element.classList.remove(className); | ||
return; | ||
} | ||
element.className = element.className.replace(new RegExp("(^|\\s+)" + className + "(\\s+|$)"), " ").replace(/^\s+/, "").replace(/\s+$/, ""); | ||
if (supportsClassList) { | ||
element.classList.remove(className); | ||
return; | ||
} | ||
element.className = element.className. | ||
replace(new RegExp("(^|\\s+)" + className + "(\\s+|$)"), " "). | ||
replace(/^\s+/, ""). | ||
replace(/\s+$/, ""); | ||
}; | ||
const callCallback = function (callback, argument) { | ||
if (callback) { | ||
callback(argument); | ||
} | ||
const callCallback = function(callback, argument) { | ||
if (callback) { | ||
callback(argument); | ||
} | ||
}; | ||
@@ -165,35 +174,41 @@ | ||
const removeListeners = function(element, loadHandler, errorHandler) { | ||
element.removeEventListener(loadString, loadHandler); | ||
element.removeEventListener(errorString, errorHandler); | ||
element.removeEventListener(loadString, loadHandler); | ||
element.removeEventListener(errorString, errorHandler); | ||
}; | ||
const addOneShotListeners = function(element, settings) { | ||
const onLoad = (event) => { | ||
onEvent(event, true, settings); | ||
removeListeners(element, onLoad, onError); | ||
}; | ||
const onError = (event) => { | ||
onEvent(event, false, settings); | ||
removeListeners(element, onLoad, onError); | ||
}; | ||
element.addEventListener(loadString, onLoad); | ||
element.addEventListener(errorString, onError); | ||
const onLoad = event => { | ||
onEvent(event, true, settings); | ||
removeListeners(element, onLoad, onError); | ||
}; | ||
const onError = event => { | ||
onEvent(event, false, settings); | ||
removeListeners(element, onLoad, onError); | ||
}; | ||
element.addEventListener(loadString, onLoad); | ||
element.addEventListener(errorString, onError); | ||
}; | ||
const onEvent = function (event, success, settings) { | ||
const element = event.target; | ||
removeClass(element, settings.class_loading); | ||
addClass(element, (success ? settings.class_loaded : settings.class_error)); // Setting loaded or error class | ||
callCallback(success ? settings.callback_load : settings.callback_error, element); // Calling loaded or error callback | ||
const onEvent = function(event, success, settings) { | ||
const element = event.target; | ||
removeClass(element, settings.class_loading); | ||
addClass(element, success ? settings.class_loaded : settings.class_error); // Setting loaded or error class | ||
callCallback( | ||
success ? settings.callback_load : settings.callback_error, | ||
element | ||
); | ||
}; | ||
function revealElement (element, settings) { | ||
callCallback(settings.callback_enter, element); | ||
if (["IMG", "IFRAME", "VIDEO"].indexOf(element.tagName) > -1) { | ||
addOneShotListeners(element, settings); | ||
addClass(element, settings.class_loading); | ||
} | ||
setSources(element, settings); | ||
setData(element, "was-processed", true); | ||
callCallback(settings.callback_set, element); | ||
function revealElement(element, settings, force) { | ||
if (!force && getWasProcessed(element)) { | ||
return; // element has already been processed and force wasn't true | ||
} | ||
callCallback(settings.callback_enter, element); | ||
if (["IMG", "IFRAME", "VIDEO"].indexOf(element.tagName) > -1) { | ||
addOneShotListeners(element, settings); | ||
addClass(element, settings.class_loading); | ||
} | ||
setSources(element, settings); | ||
setWasProcessed(element); | ||
callCallback(settings.callback_set, element); | ||
} | ||
@@ -203,67 +218,75 @@ | ||
entry.intersectionRatio is not enough alone because it could be 0 on some intersecting elements */ | ||
const isIntersecting = (element) => element.isIntersecting || element.intersectionRatio > 0; | ||
const isIntersecting = element => | ||
element.isIntersecting || element.intersectionRatio > 0; | ||
const LazyLoad = function (customSettings, elements) { | ||
this._settings = getInstanceSettings(customSettings); | ||
this._setObserver(); | ||
this.update(elements); | ||
const getObserverSettings = settings => ({ | ||
root: settings.container === document ? null : settings.container, | ||
rootMargin: settings.threshold + "px" | ||
}); | ||
const LazyLoad = function(customSettings, elements) { | ||
this._settings = getInstanceSettings(customSettings); | ||
this._setObserver(); | ||
this.update(elements); | ||
}; | ||
LazyLoad.prototype = { | ||
_setObserver: function () { | ||
if (!supportsIntersectionObserver) { | ||
return; | ||
} | ||
_setObserver: function() { | ||
if (!supportsIntersectionObserver) { | ||
return; | ||
} | ||
const revealIntersectingElements = entries => { | ||
entries.forEach(entry => { | ||
if (isIntersecting(entry)) { | ||
let element = entry.target; | ||
this.load(element); | ||
this._observer.unobserve(element); | ||
} | ||
}); | ||
this._elements = purgeElements(this._elements); | ||
}; | ||
this._observer = new IntersectionObserver( | ||
revealIntersectingElements, | ||
getObserverSettings(this._settings) | ||
); | ||
}, | ||
const settings = this._settings; | ||
const observerSettings = { | ||
root: settings.container === document ? null : settings.container, | ||
rootMargin: settings.threshold + "px" | ||
}; | ||
const revealIntersectingElements = (entries) => { | ||
entries.forEach(entry => { | ||
if (isIntersecting(entry)) { | ||
let element = entry.target; | ||
revealElement(element, this._settings); | ||
this._observer.unobserve(element); | ||
} | ||
}); | ||
this._elements = purgeElements(this._elements); | ||
}; | ||
this._observer = new IntersectionObserver(revealIntersectingElements, observerSettings); | ||
}, | ||
loadAll: function() { | ||
this._elements.forEach(element => { | ||
this.load(element); | ||
}); | ||
this._elements = purgeElements(this._elements); | ||
}, | ||
loadAll: function() { | ||
const settings = this._settings; | ||
// Fallback: load all elements at once | ||
this._elements.forEach(element => { | ||
revealElement(element, settings); | ||
}); | ||
this._elements = purgeElements(this._elements); | ||
}, | ||
update: function(elements) { | ||
const settings = this._settings; | ||
const nodeSet = | ||
elements || | ||
settings.container.querySelectorAll(settings.elements_selector); | ||
update: function (elements) { | ||
const settings = this._settings; | ||
const nodeSet = elements || settings.container.querySelectorAll(settings.elements_selector); | ||
this._elements = purgeElements(Array.prototype.slice.call(nodeSet)); // nodeset to array for IE compatibility | ||
if (this._observer) { | ||
this._elements.forEach(element => { | ||
this._observer.observe(element); | ||
}); | ||
return; | ||
} | ||
// Fallback: load all elements at once | ||
this.loadAll(); | ||
}, | ||
this._elements = purgeElements(Array.prototype.slice.call(nodeSet)); // nodeset to array for IE compatibility | ||
if (this._observer) { | ||
this._elements.forEach(element => { | ||
this._observer.observe(element); | ||
}); | ||
return; | ||
} | ||
this.loadAll(); | ||
}, | ||
destroy: function() { | ||
if (this._observer) { | ||
purgeElements(this._elements).forEach(element => { | ||
this._observer.unobserve(element); | ||
}); | ||
this._observer = null; | ||
} | ||
this._elements = null; | ||
this._settings = null; | ||
}, | ||
destroy: function () { | ||
if (this._observer) { | ||
purgeElements(this._elements).forEach(element => { | ||
this._observer.unobserve(element); | ||
}); | ||
this._observer = null; | ||
} | ||
this._elements = null; | ||
this._settings = null; | ||
} | ||
load: function(element, force) { | ||
revealElement(element, this._settings, force); | ||
} | ||
}; | ||
@@ -274,3 +297,3 @@ | ||
if (runningOnBrowser && autoInitOptions) { | ||
autoInitialize(LazyLoad, autoInitOptions); | ||
autoInitialize(LazyLoad, autoInitOptions); | ||
} | ||
@@ -277,0 +300,0 @@ |
@@ -6,274 +6,294 @@ var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; | ||
(function (global, factory) { | ||
(typeof exports === 'undefined' ? 'undefined' : _typeof(exports)) === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : global.LazyLoad = factory(); | ||
(typeof exports === 'undefined' ? 'undefined' : _typeof(exports)) === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : global.LazyLoad = factory(); | ||
})(this, function () { | ||
'use strict'; | ||
'use strict'; | ||
var getInstanceSettings = function getInstanceSettings(customSettings) { | ||
var defaultSettings = { | ||
elements_selector: "img", | ||
container: document, | ||
threshold: 300, | ||
data_src: "src", | ||
data_srcset: "srcset", | ||
data_sizes: "sizes", | ||
class_loading: "loading", | ||
class_loaded: "loaded", | ||
class_error: "error", | ||
callback_load: null, | ||
callback_error: null, | ||
callback_set: null, | ||
callback_enter: null | ||
}; | ||
var getInstanceSettings = function getInstanceSettings(customSettings) { | ||
var defaultSettings = { | ||
elements_selector: "img", | ||
container: document, | ||
threshold: 300, | ||
data_src: "src", | ||
data_srcset: "srcset", | ||
data_sizes: "sizes", | ||
class_loading: "loading", | ||
class_loaded: "loaded", | ||
class_error: "error", | ||
callback_load: null, | ||
callback_error: null, | ||
callback_set: null, | ||
callback_enter: null | ||
}; | ||
return _extends({}, defaultSettings, customSettings); | ||
}; | ||
return _extends({}, defaultSettings, customSettings); | ||
}; | ||
var dataPrefix = "data-"; | ||
var dataPrefix = "data-"; | ||
var processedDataName = "was-processed"; | ||
var processedDataValue = "true"; | ||
var getData = function getData(element, attribute) { | ||
return element.getAttribute(dataPrefix + attribute); | ||
}; | ||
var getData = function getData(element, attribute) { | ||
return element.getAttribute(dataPrefix + attribute); | ||
}; | ||
var setData = function setData(element, attribute, value) { | ||
return element.setAttribute(dataPrefix + attribute, value); | ||
}; | ||
var setData = function setData(element, attribute, value) { | ||
return element.setAttribute(dataPrefix + attribute, value); | ||
}; | ||
function purgeElements(elements) { | ||
return elements.filter(function (element) { | ||
return !getData(element, "was-processed"); | ||
}); | ||
} | ||
var setWasProcessed = function setWasProcessed(element) { | ||
return setData(element, processedDataName, processedDataValue); | ||
}; | ||
/* Creates instance and notifies it through the window element */ | ||
var createInstance = function createInstance(classObj, options) { | ||
var event; | ||
var eventString = "LazyLoad::Initialized"; | ||
var instance = new classObj(options); | ||
try { | ||
// Works in modern browsers | ||
event = new CustomEvent(eventString, { detail: { instance: instance } }); | ||
} catch (err) { | ||
// Works in Internet Explorer (all versions) | ||
event = document.createEvent("CustomEvent"); | ||
event.initCustomEvent(eventString, false, false, { instance: instance }); | ||
} | ||
window.dispatchEvent(event); | ||
}; | ||
var getWasProcessed = function getWasProcessed(element) { | ||
return getData(element, processedDataName) === processedDataValue; | ||
}; | ||
/* Auto initialization of one or more instances of lazyload, depending on the | ||
options passed in (plain object or an array) */ | ||
function autoInitialize(classObj, options) { | ||
if (!options.length) { | ||
// Plain object | ||
createInstance(classObj, options); | ||
} else { | ||
// Array of objects | ||
for (var i = 0, optionsItem; optionsItem = options[i]; i += 1) { | ||
createInstance(classObj, optionsItem); | ||
} | ||
} | ||
} | ||
function purgeElements(elements) { | ||
return elements.filter(function (element) { | ||
return !getWasProcessed(element); | ||
}); | ||
} | ||
var setSourcesInChildren = function setSourcesInChildren(parentTag, attrName, dataAttrName) { | ||
for (var i = 0, childTag; childTag = parentTag.children[i]; i += 1) { | ||
if (childTag.tagName === "SOURCE") { | ||
var attributeValue = getData(childTag, dataAttrName); | ||
if (attributeValue) { | ||
childTag.setAttribute(attrName, attributeValue); | ||
} | ||
} | ||
} | ||
}; | ||
/* Creates instance and notifies it through the window element */ | ||
var createInstance = function createInstance(classObj, options) { | ||
var event; | ||
var eventString = "LazyLoad::Initialized"; | ||
var instance = new classObj(options); | ||
try { | ||
// Works in modern browsers | ||
event = new CustomEvent(eventString, { detail: { instance: instance } }); | ||
} catch (err) { | ||
// Works in Internet Explorer (all versions) | ||
event = document.createEvent("CustomEvent"); | ||
event.initCustomEvent(eventString, false, false, { instance: instance }); | ||
} | ||
window.dispatchEvent(event); | ||
}; | ||
var setAttributeIfNotNullOrEmpty = function setAttributeIfNotNullOrEmpty(element, attrName, value) { | ||
if (!value) { | ||
return; | ||
} | ||
element.setAttribute(attrName, value); | ||
}; | ||
/* Auto initialization of one or more instances of lazyload, depending on the | ||
options passed in (plain object or an array) */ | ||
function autoInitialize(classObj, options) { | ||
if (!options.length) { | ||
// Plain object | ||
createInstance(classObj, options); | ||
} else { | ||
// Array of objects | ||
for (var i = 0, optionsItem; optionsItem = options[i]; i += 1) { | ||
createInstance(classObj, optionsItem); | ||
} | ||
} | ||
} | ||
var setSources = function setSources(element, settings) { | ||
var sizesDataName = settings.data_sizes, | ||
srcsetDataName = settings.data_srcset, | ||
srcDataName = settings.data_src; | ||
var setSourcesInChildren = function setSourcesInChildren(parentTag, attrName, dataAttrName) { | ||
for (var i = 0, childTag; childTag = parentTag.children[i]; i += 1) { | ||
if (childTag.tagName === "SOURCE") { | ||
var attributeValue = getData(childTag, dataAttrName); | ||
if (attributeValue) { | ||
childTag.setAttribute(attrName, attributeValue); | ||
} | ||
} | ||
} | ||
}; | ||
var srcDataValue = getData(element, srcDataName); | ||
var tagName = element.tagName; | ||
if (tagName === "IMG") { | ||
var parent = element.parentNode; | ||
if (parent && parent.tagName === "PICTURE") { | ||
setSourcesInChildren(parent, "srcset", srcsetDataName); | ||
} | ||
var sizesDataValue = getData(element, sizesDataName); | ||
setAttributeIfNotNullOrEmpty(element, "sizes", sizesDataValue); | ||
var srcsetDataValue = getData(element, srcsetDataName); | ||
setAttributeIfNotNullOrEmpty(element, "srcset", srcsetDataValue); | ||
setAttributeIfNotNullOrEmpty(element, "src", srcDataValue); | ||
return; | ||
} | ||
if (tagName === "IFRAME") { | ||
setAttributeIfNotNullOrEmpty(element, "src", srcDataValue); | ||
return; | ||
} | ||
if (tagName === "VIDEO") { | ||
setSourcesInChildren(element, "src", srcDataName); | ||
setAttributeIfNotNullOrEmpty(element, "src", srcDataValue); | ||
return; | ||
} | ||
if (srcDataValue) { | ||
element.style.backgroundImage = 'url("' + srcDataValue + '")'; | ||
} | ||
}; | ||
var setAttributeIfNotNullOrEmpty = function setAttributeIfNotNullOrEmpty(element, attrName, value) { | ||
if (!value) { | ||
return; | ||
} | ||
element.setAttribute(attrName, value); | ||
}; | ||
var runningOnBrowser = typeof window !== "undefined"; | ||
var setSources = function setSources(element, settings) { | ||
var sizesDataName = settings.data_sizes, | ||
srcsetDataName = settings.data_srcset, | ||
srcDataName = settings.data_src; | ||
var supportsIntersectionObserver = runningOnBrowser && "IntersectionObserver" in window; | ||
var srcDataValue = getData(element, srcDataName); | ||
switch (element.tagName) { | ||
case "IMG": | ||
{ | ||
var parent = element.parentNode; | ||
if (parent && parent.tagName === "PICTURE") { | ||
setSourcesInChildren(parent, "srcset", srcsetDataName); | ||
} | ||
var sizesDataValue = getData(element, sizesDataName); | ||
setAttributeIfNotNullOrEmpty(element, "sizes", sizesDataValue); | ||
var srcsetDataValue = getData(element, srcsetDataName); | ||
setAttributeIfNotNullOrEmpty(element, "srcset", srcsetDataValue); | ||
setAttributeIfNotNullOrEmpty(element, "src", srcDataValue); | ||
break; | ||
} | ||
case "IFRAME": | ||
setAttributeIfNotNullOrEmpty(element, "src", srcDataValue); | ||
break; | ||
case "VIDEO": | ||
setSourcesInChildren(element, "src", srcDataName); | ||
setAttributeIfNotNullOrEmpty(element, "src", srcDataValue); | ||
break; | ||
default: | ||
if (srcDataValue) { | ||
element.style.backgroundImage = 'url("' + srcDataValue + '")'; | ||
} | ||
} | ||
}; | ||
var supportsClassList = runningOnBrowser && "classList" in document.createElement("p"); | ||
var runningOnBrowser = typeof window !== "undefined"; | ||
var addClass = function addClass(element, className) { | ||
if (supportsClassList) { | ||
element.classList.add(className); | ||
return; | ||
} | ||
element.className += (element.className ? " " : "") + className; | ||
}; | ||
var supportsIntersectionObserver = runningOnBrowser && "IntersectionObserver" in window; | ||
var removeClass = function removeClass(element, className) { | ||
if (supportsClassList) { | ||
element.classList.remove(className); | ||
return; | ||
} | ||
element.className = element.className.replace(new RegExp("(^|\\s+)" + className + "(\\s+|$)"), " ").replace(/^\s+/, "").replace(/\s+$/, ""); | ||
}; | ||
var supportsClassList = runningOnBrowser && "classList" in document.createElement("p"); | ||
var callCallback = function callCallback(callback, argument) { | ||
if (callback) { | ||
callback(argument); | ||
} | ||
}; | ||
var addClass = function addClass(element, className) { | ||
if (supportsClassList) { | ||
element.classList.add(className); | ||
return; | ||
} | ||
element.className += (element.className ? " " : "") + className; | ||
}; | ||
var loadString = "load"; | ||
var errorString = "error"; | ||
var removeClass = function removeClass(element, className) { | ||
if (supportsClassList) { | ||
element.classList.remove(className); | ||
return; | ||
} | ||
element.className = element.className.replace(new RegExp("(^|\\s+)" + className + "(\\s+|$)"), " ").replace(/^\s+/, "").replace(/\s+$/, ""); | ||
}; | ||
var removeListeners = function removeListeners(element, loadHandler, errorHandler) { | ||
element.removeEventListener(loadString, loadHandler); | ||
element.removeEventListener(errorString, errorHandler); | ||
}; | ||
var callCallback = function callCallback(callback, argument) { | ||
if (callback) { | ||
callback(argument); | ||
} | ||
}; | ||
var addOneShotListeners = function addOneShotListeners(element, settings) { | ||
var onLoad = function onLoad(event) { | ||
onEvent(event, true, settings); | ||
removeListeners(element, onLoad, onError); | ||
}; | ||
var onError = function onError(event) { | ||
onEvent(event, false, settings); | ||
removeListeners(element, onLoad, onError); | ||
}; | ||
element.addEventListener(loadString, onLoad); | ||
element.addEventListener(errorString, onError); | ||
}; | ||
var loadString = "load"; | ||
var errorString = "error"; | ||
var onEvent = function onEvent(event, success, settings) { | ||
var element = event.target; | ||
removeClass(element, settings.class_loading); | ||
addClass(element, success ? settings.class_loaded : settings.class_error); // Setting loaded or error class | ||
callCallback(success ? settings.callback_load : settings.callback_error, element); // Calling loaded or error callback | ||
}; | ||
var removeListeners = function removeListeners(element, loadHandler, errorHandler) { | ||
element.removeEventListener(loadString, loadHandler); | ||
element.removeEventListener(errorString, errorHandler); | ||
}; | ||
function revealElement(element, settings) { | ||
callCallback(settings.callback_enter, element); | ||
if (["IMG", "IFRAME", "VIDEO"].indexOf(element.tagName) > -1) { | ||
addOneShotListeners(element, settings); | ||
addClass(element, settings.class_loading); | ||
} | ||
setSources(element, settings); | ||
setData(element, "was-processed", true); | ||
callCallback(settings.callback_set, element); | ||
} | ||
var addOneShotListeners = function addOneShotListeners(element, settings) { | ||
var onLoad = function onLoad(event) { | ||
onEvent(event, true, settings); | ||
removeListeners(element, onLoad, onError); | ||
}; | ||
var onError = function onError(event) { | ||
onEvent(event, false, settings); | ||
removeListeners(element, onLoad, onError); | ||
}; | ||
element.addEventListener(loadString, onLoad); | ||
element.addEventListener(errorString, onError); | ||
}; | ||
/* entry.isIntersecting needs fallback because is null on some versions of MS Edge, and | ||
entry.intersectionRatio is not enough alone because it could be 0 on some intersecting elements */ | ||
var isIntersecting = function isIntersecting(element) { | ||
return element.isIntersecting || element.intersectionRatio > 0; | ||
}; | ||
var onEvent = function onEvent(event, success, settings) { | ||
var element = event.target; | ||
removeClass(element, settings.class_loading); | ||
addClass(element, success ? settings.class_loaded : settings.class_error); // Setting loaded or error class | ||
callCallback(success ? settings.callback_load : settings.callback_error, element); | ||
}; | ||
var LazyLoad = function LazyLoad(customSettings, elements) { | ||
this._settings = getInstanceSettings(customSettings); | ||
this._setObserver(); | ||
this.update(elements); | ||
}; | ||
function revealElement(element, settings, force) { | ||
if (!force && getWasProcessed(element)) { | ||
return; // element has already been processed and force wasn't true | ||
} | ||
callCallback(settings.callback_enter, element); | ||
if (["IMG", "IFRAME", "VIDEO"].indexOf(element.tagName) > -1) { | ||
addOneShotListeners(element, settings); | ||
addClass(element, settings.class_loading); | ||
} | ||
setSources(element, settings); | ||
setWasProcessed(element); | ||
callCallback(settings.callback_set, element); | ||
} | ||
LazyLoad.prototype = { | ||
_setObserver: function _setObserver() { | ||
var _this = this; | ||
/* entry.isIntersecting needs fallback because is null on some versions of MS Edge, and | ||
entry.intersectionRatio is not enough alone because it could be 0 on some intersecting elements */ | ||
var isIntersecting = function isIntersecting(element) { | ||
return element.isIntersecting || element.intersectionRatio > 0; | ||
}; | ||
if (!supportsIntersectionObserver) { | ||
return; | ||
} | ||
var getObserverSettings = function getObserverSettings(settings) { | ||
return { | ||
root: settings.container === document ? null : settings.container, | ||
rootMargin: settings.threshold + "px" | ||
}; | ||
}; | ||
var settings = this._settings; | ||
var observerSettings = { | ||
root: settings.container === document ? null : settings.container, | ||
rootMargin: settings.threshold + "px" | ||
}; | ||
var revealIntersectingElements = function revealIntersectingElements(entries) { | ||
entries.forEach(function (entry) { | ||
if (isIntersecting(entry)) { | ||
var element = entry.target; | ||
revealElement(element, _this._settings); | ||
_this._observer.unobserve(element); | ||
} | ||
}); | ||
_this._elements = purgeElements(_this._elements); | ||
}; | ||
this._observer = new IntersectionObserver(revealIntersectingElements, observerSettings); | ||
}, | ||
var LazyLoad = function LazyLoad(customSettings, elements) { | ||
this._settings = getInstanceSettings(customSettings); | ||
this._setObserver(); | ||
this.update(elements); | ||
}; | ||
loadAll: function loadAll() { | ||
var settings = this._settings; | ||
// Fallback: load all elements at once | ||
this._elements.forEach(function (element) { | ||
revealElement(element, settings); | ||
}); | ||
this._elements = purgeElements(this._elements); | ||
}, | ||
LazyLoad.prototype = { | ||
_setObserver: function _setObserver() { | ||
var _this = this; | ||
update: function update(elements) { | ||
var _this2 = this; | ||
if (!supportsIntersectionObserver) { | ||
return; | ||
} | ||
var revealIntersectingElements = function revealIntersectingElements(entries) { | ||
entries.forEach(function (entry) { | ||
if (isIntersecting(entry)) { | ||
var element = entry.target; | ||
_this.load(element); | ||
_this._observer.unobserve(element); | ||
} | ||
}); | ||
_this._elements = purgeElements(_this._elements); | ||
}; | ||
this._observer = new IntersectionObserver(revealIntersectingElements, getObserverSettings(this._settings)); | ||
}, | ||
var settings = this._settings; | ||
var nodeSet = elements || settings.container.querySelectorAll(settings.elements_selector); | ||
loadAll: function loadAll() { | ||
var _this2 = this; | ||
this._elements = purgeElements(Array.prototype.slice.call(nodeSet)); // nodeset to array for IE compatibility | ||
if (this._observer) { | ||
this._elements.forEach(function (element) { | ||
_this2._observer.observe(element); | ||
}); | ||
return; | ||
} | ||
this.loadAll(); | ||
}, | ||
this._elements.forEach(function (element) { | ||
_this2.load(element); | ||
}); | ||
this._elements = purgeElements(this._elements); | ||
}, | ||
destroy: function destroy() { | ||
var _this3 = this; | ||
update: function update(elements) { | ||
var _this3 = this; | ||
if (this._observer) { | ||
purgeElements(this._elements).forEach(function (element) { | ||
_this3._observer.unobserve(element); | ||
}); | ||
this._observer = null; | ||
} | ||
this._elements = null; | ||
this._settings = null; | ||
} | ||
}; | ||
var settings = this._settings; | ||
var nodeSet = elements || settings.container.querySelectorAll(settings.elements_selector); | ||
/* Automatic instances creation if required (useful for async script loading!) */ | ||
var autoInitOptions = window.lazyLoadOptions; | ||
if (runningOnBrowser && autoInitOptions) { | ||
autoInitialize(LazyLoad, autoInitOptions); | ||
} | ||
this._elements = purgeElements(Array.prototype.slice.call(nodeSet)); // nodeset to array for IE compatibility | ||
if (this._observer) { | ||
this._elements.forEach(function (element) { | ||
_this3._observer.observe(element); | ||
}); | ||
return; | ||
} | ||
// Fallback: load all elements at once | ||
this.loadAll(); | ||
}, | ||
return LazyLoad; | ||
destroy: function destroy() { | ||
var _this4 = this; | ||
if (this._observer) { | ||
purgeElements(this._elements).forEach(function (element) { | ||
_this4._observer.unobserve(element); | ||
}); | ||
this._observer = null; | ||
} | ||
this._elements = null; | ||
this._settings = null; | ||
}, | ||
load: function load(element, force) { | ||
revealElement(element, this._settings, force); | ||
} | ||
}; | ||
/* Automatic instances creation if required (useful for async script loading!) */ | ||
var autoInitOptions = window.lazyLoadOptions; | ||
if (runningOnBrowser && autoInitOptions) { | ||
autoInitialize(LazyLoad, autoInitOptions); | ||
} | ||
return LazyLoad; | ||
}); |
@@ -1,1 +0,1 @@ | ||
var _extends=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},_typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};!function(e,t){"object"===("undefined"==typeof exports?"undefined":_typeof(exports))&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.LazyLoad=t()}(this,function(){"use strict";var r="data-",u=function(e,t){return e.getAttribute(r+t)},n=function(e,t,n){return e.setAttribute(r+t,n)};function s(e){return e.filter(function(e){return!u(e,"was-processed")})}var o=function(e,t){var n,r="LazyLoad::Initialized",s=new e(t);try{n=new CustomEvent(r,{detail:{instance:s}})}catch(e){(n=document.createEvent("CustomEvent")).initCustomEvent(r,!1,!1,{instance:s})}window.dispatchEvent(n)};var d=function(e,t,n){for(var r,s=0;r=e.children[s];s+=1)if("SOURCE"===r.tagName){var o=u(r,n);o&&r.setAttribute(t,o)}},f=function(e,t,n){n&&e.setAttribute(t,n)},a=function(e,t){var n=t.data_sizes,r=t.data_srcset,s=t.data_src,o=u(e,s),a=e.tagName;if("IMG"===a){var i=e.parentNode;i&&"PICTURE"===i.tagName&&d(i,"srcset",r);var c=u(e,n);f(e,"sizes",c);var l=u(e,r);return f(e,"srcset",l),void f(e,"src",o)}if("IFRAME"!==a)return"VIDEO"===a?(d(e,"src",s),void f(e,"src",o)):void(o&&(e.style.backgroundImage='url("'+o+'")'));f(e,"src",o)},e="undefined"!=typeof window,i=e&&"IntersectionObserver"in window,c=e&&"classList"in document.createElement("p"),l=function(e,t){c?e.classList.add(t):e.className+=(e.className?" ":"")+t},v=function(e,t){e&&e(t)},_="load",m="error",b=function(e,t,n){e.removeEventListener(_,t),e.removeEventListener(m,n)},h=function(n,r){var s=function e(t){p(t,!0,r),b(n,e,o)},o=function e(t){p(t,!1,r),b(n,s,e)};n.addEventListener(_,s),n.addEventListener(m,o)},p=function(e,t,n){var r,s,o=e.target;r=o,s=n.class_loading,c?r.classList.remove(s):r.className=r.className.replace(new RegExp("(^|\\s+)"+s+"(\\s+|$)")," ").replace(/^\s+/,"").replace(/\s+$/,""),l(o,t?n.class_loaded:n.class_error),v(t?n.callback_load:n.callback_error,o)};function y(e,t){v(t.callback_enter,e),-1<["IMG","IFRAME","VIDEO"].indexOf(e.tagName)&&(h(e,t),l(e,t.class_loading)),a(e,t),n(e,"was-processed",!0),v(t.callback_set,e)}var t=function(e,t){var n;this._settings=(n={elements_selector:"img",container:document,threshold:300,data_src:"src",data_srcset:"srcset",data_sizes:"sizes",class_loading:"loading",class_loaded:"loaded",class_error:"error",callback_load:null,callback_error:null,callback_set:null,callback_enter:null},_extends({},n,e)),this._setObserver(),this.update(t)};t.prototype={_setObserver:function(){var r=this;if(i){var e=this._settings,t={root:e.container===document?null:e.container,rootMargin:e.threshold+"px"};this._observer=new IntersectionObserver(function(e){e.forEach(function(e){if((n=e).isIntersecting||0<n.intersectionRatio){var t=e.target;y(t,r._settings),r._observer.unobserve(t)}var n}),r._elements=s(r._elements)},t)}},loadAll:function(){var t=this._settings;this._elements.forEach(function(e){y(e,t)}),this._elements=s(this._elements)},update:function(e){var t=this,n=this._settings,r=e||n.container.querySelectorAll(n.elements_selector);this._elements=s(Array.prototype.slice.call(r)),this._observer?this._elements.forEach(function(e){t._observer.observe(e)}):this.loadAll()},destroy:function(){var t=this;this._observer&&(s(this._elements).forEach(function(e){t._observer.unobserve(e)}),this._observer=null),this._elements=null,this._settings=null}};var g=window.lazyLoadOptions;return e&&g&&function(e,t){if(t.length)for(var n,r=0;n=t[r];r+=1)o(e,n);else o(e,t)}(t,g),t}); | ||
var _extends=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},_typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};!function(e,t){"object"===("undefined"==typeof exports?"undefined":_typeof(exports))&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.LazyLoad=t()}(this,function(){"use strict";var r="data-",s="was-processed",a="true",l=function(e,t){return e.getAttribute(r+t)},o=function(e){return t=s,n=a,e.setAttribute(r+t,n);var t,n},i=function(e){return l(e,s)===a};function c(e){return e.filter(function(e){return!i(e)})}var u=function(e,t){var n,r="LazyLoad::Initialized",s=new e(t);try{n=new CustomEvent(r,{detail:{instance:s}})}catch(e){(n=document.createEvent("CustomEvent")).initCustomEvent(r,!1,!1,{instance:s})}window.dispatchEvent(n)};var d=function(e,t,n){for(var r,s=0;r=e.children[s];s+=1)if("SOURCE"===r.tagName){var a=l(r,n);a&&r.setAttribute(t,a)}},f=function(e,t,n){n&&e.setAttribute(t,n)},v=function(e,t){var n=t.data_sizes,r=t.data_srcset,s=t.data_src,a=l(e,s);switch(e.tagName){case"IMG":var o=e.parentNode;o&&"PICTURE"===o.tagName&&d(o,"srcset",r);var i=l(e,n);f(e,"sizes",i);var c=l(e,r);f(e,"srcset",c),f(e,"src",a);break;case"IFRAME":f(e,"src",a);break;case"VIDEO":d(e,"src",s),f(e,"src",a);break;default:a&&(e.style.backgroundImage='url("'+a+'")')}},e="undefined"!=typeof window,t=e&&"IntersectionObserver"in window,_=e&&"classList"in document.createElement("p"),m=function(e,t){_?e.classList.add(t):e.className+=(e.className?" ":"")+t},b=function(e,t){e&&e(t)},h="load",p="error",y=function(e,t,n){e.removeEventListener(h,t),e.removeEventListener(p,n)},g=function(n,r){var s=function e(t){E(t,!0,r),y(n,e,a)},a=function e(t){E(t,!1,r),y(n,s,e)};n.addEventListener(h,s),n.addEventListener(p,a)},E=function(e,t,n){var r,s,a=e.target;r=a,s=n.class_loading,_?r.classList.remove(s):r.className=r.className.replace(new RegExp("(^|\\s+)"+s+"(\\s+|$)")," ").replace(/^\s+/,"").replace(/\s+$/,""),m(a,t?n.class_loaded:n.class_error),b(t?n.callback_load:n.callback_error,a)};var n=function(e,t){var n;this._settings=(n={elements_selector:"img",container:document,threshold:300,data_src:"src",data_srcset:"srcset",data_sizes:"sizes",class_loading:"loading",class_loaded:"loaded",class_error:"error",callback_load:null,callback_error:null,callback_set:null,callback_enter:null},_extends({},n,e)),this._setObserver(),this.update(t)};n.prototype={_setObserver:function(){var r=this;if(t){var e;this._observer=new IntersectionObserver(function(e){e.forEach(function(e){if((n=e).isIntersecting||0<n.intersectionRatio){var t=e.target;r.load(t),r._observer.unobserve(t)}var n}),r._elements=c(r._elements)},{root:(e=this._settings).container===document?null:e.container,rootMargin:e.threshold+"px"})}},loadAll:function(){var t=this;this._elements.forEach(function(e){t.load(e)}),this._elements=c(this._elements)},update:function(e){var t=this,n=this._settings,r=e||n.container.querySelectorAll(n.elements_selector);this._elements=c(Array.prototype.slice.call(r)),this._observer?this._elements.forEach(function(e){t._observer.observe(e)}):this.loadAll()},destroy:function(){var t=this;this._observer&&(c(this._elements).forEach(function(e){t._observer.unobserve(e)}),this._observer=null),this._elements=null,this._settings=null},load:function(e,t){var n,r;n=e,r=this._settings,!t&&i(n)||(b(r.callback_enter,n),-1<["IMG","IFRAME","VIDEO"].indexOf(n.tagName)&&(g(n,r),m(n,r.class_loading)),v(n,r),o(n),b(r.callback_set,n))}};var w=window.lazyLoadOptions;return e&&w&&function(e,t){if(t.length)for(var n,r=0;n=t[r];r+=1)u(e,n);else u(e,t)}(n,w),n}); |
@@ -10,30 +10,34 @@ var gulp = require("gulp"); | ||
gulp.task("default", function () { | ||
process.env.NODE_ENV = "release"; | ||
return gulp.src("./src/**/*.js") | ||
// ----------- linting -------------- | ||
.pipe(eslint()) | ||
.pipe(eslint.format()) | ||
.pipe(eslint.failAfterError()) // --> failing if errors | ||
// ----------- rolling up -------------- | ||
.pipe(rollup({ | ||
format: "umd", | ||
moduleName: "LazyLoad", | ||
entry: "./src/lazyload.js" | ||
})) | ||
.pipe(rename("lazyload.es2015.js")) | ||
.pipe(gulp.dest(destFolder)) // --> writing rolledup | ||
// ----------- babelizing -------------- | ||
.pipe(babel()) | ||
.pipe(rename("lazyload.js")) | ||
.pipe(gulp.dest(destFolder)) // --> writing babelized ES5 | ||
// ----------- minifying -------------- | ||
.pipe(uglify()) | ||
.pipe(rename("lazyload.min.js")) | ||
.pipe(gulp.dest(destFolder)); // --> writing uglified | ||
gulp.task("default", function() { | ||
process.env.NODE_ENV = "release"; | ||
return ( | ||
gulp. | ||
src("./src/**/*.js"). | ||
// ----------- linting -------------- | ||
pipe(eslint()). | ||
pipe(eslint.format()). | ||
pipe(eslint.failAfterError()). // --> failing if errors | ||
// ----------- rolling up -------------- | ||
pipe( | ||
rollup({ | ||
output: { name: "LazyLoad", format: "umd" }, | ||
input: "./src/lazyload.js" | ||
}) | ||
). | ||
pipe(rename("lazyload.es2015.js")). | ||
pipe(gulp.dest(destFolder)). // --> writing rolledup | ||
// ----------- babelizing -------------- | ||
pipe(babel()). | ||
pipe(rename("lazyload.js")). | ||
pipe(gulp.dest(destFolder)). // --> writing babelized ES5 | ||
// ----------- minifying -------------- | ||
pipe(uglify()). | ||
pipe(rename("lazyload.min.js")). | ||
pipe(gulp.dest(destFolder)) // --> writing uglified | ||
); | ||
}); | ||
gulp.task("watch", function () { | ||
gulp.watch("./src/**/*.js", ["default"]); | ||
// Other watchers | ||
}); | ||
gulp.task("watch", function() { | ||
gulp.watch("./src/**/*.js", ["default"]); | ||
// Other watchers | ||
}); |
{ | ||
"name": "vanilla-lazyload", | ||
"version": "10.9.0", | ||
"version": "10.10.0", | ||
"description": "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", | ||
@@ -5,0 +5,0 @@ "main": "dist/lazyload.min.js", |
@@ -15,3 +15,3 @@ LazyLoad is a fast, lightweight and flexible script that _speeds up your web application_ by **loading images, video or iframes as they enter the viewport**. It's written in plain "vanilla" JavaScript, uses [Intersection Observers](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API), and supports [responsive images](https://alistapart.com/article/responsive-images-in-practice). It's also SEO-friendly and it has some other [notable features](#notable-features). | ||
```html | ||
<script src="https://cdnjs.cloudflare.com/ajax/libs/vanilla-lazyload/8.9.0/lazyload.min.js"></script> | ||
<script src="https://cdnjs.cloudflare.com/ajax/libs/vanilla-lazyload/8.10.0/lazyload.min.js"></script> | ||
``` | ||
@@ -52,6 +52,6 @@ | ||
- **install it with npm** | ||
Recommended version `npm install vanilla-lazyload@8.9.0` | ||
Recommended version `npm install vanilla-lazyload@8.10.0` | ||
Latest version `npm install vanilla-lazyload` | ||
- **install it with bower** | ||
Recommended version `bower install vanilla-lazyload#8.9.0` | ||
Recommended version `bower install vanilla-lazyload#8.10.0` | ||
Latest version `bower install vanilla-lazyload` | ||
@@ -533,2 +533,26 @@ | ||
### Constructor arguments | ||
The `new LazyLoad()` instruction you execute on your page can take 2 parameters | ||
| Required | What to pass | Type | Default value | | ||
| -------- | ----------------------------------------------- | ------- | ------------- | | ||
| No | The option object for this instance of LazyLoad | Plain Object | `{}` | | ||
| No | A NodeSet of elements to execute LazyLoad on | NodeSet | `null` | | ||
The most common usage of LazyLoad constructor is to pass only the options object (see "options" in the next section). For example: | ||
```js | ||
var lazyLoadOptions = { /* options here */ }; | ||
var aLazyLoad = new LazyLoad(lazyLoadOptions); | ||
``` | ||
In the rare cases where you can't or don't want to select the elements using `elements_selector` and you have a reference variable to your elements set (can be a NodeSet or an array of elements), you can pass the elements set as second parameter. | ||
```js | ||
var lazyLoadOptions = { /* options here */ }; | ||
var elementsToLazyLoad = getElementSetFromSomewhere(); | ||
var aLazyLoad = new LazyLoad(lazyLoadOptions, elementsToLazyLoad); | ||
``` | ||
### Options | ||
@@ -546,2 +570,3 @@ | ||
| `data_srcset` | The name of the data attribute containing the original image source set in either `img` and `source` tags, excluding the `"data-"` part. E.g. if your data attribute is named `"data-original-set"`, just pass `"original-set"` | `"srcset"` | | ||
| `data_sizes` | The name of the data attribute containing the sizes attribute to use, excluding the `"data-"` part. E.g. if your data attribute is named `"data-sizes"`, just pass `"sizes"` | `"sizes"` | | ||
| `class_loading` | The class applied to the elements while the loading is in progress. | `"loading"` | | ||
@@ -559,7 +584,8 @@ | `class_loaded` | The class applied to the elements when the loading is complete | `"loaded"` | | ||
| Method name | Effect | | ||
|------------------|------------------------------------------------------------------------------------------------------| | ||
| `update()` | Tells _LazyLoad_ that new lazy images have arrived in the container, so it must start to manage them. | | ||
| `loadAll()` | Forces _LazyLoad_ to load all the images at once, instead of lazily loading them. | | ||
| `destroy()` | Destroys the instance, unsetting instance variables and removing listeners. | | ||
| Method name | Effect | | ||
|------------------|-------------------------------------------------------------------------------------------------------| | ||
| `update()` | Make LazyLoad to check for new lazy images in the container, using the `elements_selector` option. | | ||
| `loadAll()` | Loads all the lazy images right away, no matter if they are inside or outside the viewport. | | ||
| `load(element, force)` | Immediately loads any lazy `element`, even if it isn't selectable by the `elements_selector` option. Note that this method works only once on a specific `element`, unless you force it passing `true` as second parameter. | | ||
| `destroy()` | Destroys the instance, unsetting instance variables and removing listeners. | | ||
@@ -566,0 +592,0 @@ ## Notable features |
@@ -1,17 +0,20 @@ | ||
import {supportsClassList} from "./lazyload.environment"; | ||
import { supportsClassList } from "./lazyload.environment"; | ||
export const addClass = (element, className) => { | ||
if (supportsClassList) { | ||
element.classList.add(className); | ||
return; | ||
} | ||
element.className += (element.className ? " " : "") + className; | ||
if (supportsClassList) { | ||
element.classList.add(className); | ||
return; | ||
} | ||
element.className += (element.className ? " " : "") + className; | ||
}; | ||
export const removeClass = (element, className) => { | ||
if (supportsClassList) { | ||
element.classList.remove(className); | ||
return; | ||
} | ||
element.className = element.className.replace(new RegExp("(^|\\s+)" + className + "(\\s+|$)"), " ").replace(/^\s+/, "").replace(/\s+$/, ""); | ||
}; | ||
if (supportsClassList) { | ||
element.classList.remove(className); | ||
return; | ||
} | ||
element.className = element.className. | ||
replace(new RegExp("(^|\\s+)" + className + "(\\s+|$)"), " "). | ||
replace(/^\s+/, ""). | ||
replace(/\s+$/, ""); | ||
}; |
const dataPrefix = "data-"; | ||
const processedDataName = "was-processed"; | ||
const processedDataValue = "true"; | ||
export const getData = (element, attribute) => { | ||
return element.getAttribute(dataPrefix + attribute); | ||
} | ||
return element.getAttribute(dataPrefix + attribute); | ||
}; | ||
export const setData = (element, attribute, value) => { | ||
return element.setAttribute(dataPrefix + attribute, value); | ||
} | ||
return element.setAttribute(dataPrefix + attribute, value); | ||
}; | ||
export const setWasProcessed = element => | ||
setData(element, processedDataName, processedDataValue); | ||
export const getWasProcessed = element => | ||
getData(element, processedDataName) === processedDataValue; |
/* entry.isIntersecting needs fallback because is null on some versions of MS Edge, and | ||
entry.intersectionRatio is not enough alone because it could be 0 on some intersecting elements */ | ||
export const isIntersecting = (element) => element.isIntersecting || element.intersectionRatio > 0; | ||
export const isIntersecting = element => | ||
element.isIntersecting || element.intersectionRatio > 0; | ||
export const getObserverSettings = settings => ({ | ||
root: settings.container === document ? null : settings.container, | ||
rootMargin: settings.threshold + "px" | ||
}); |
@@ -5,69 +5,77 @@ import getInstanceSettings from "./lazyload.defaults"; | ||
import revealElement from "./lazyload.reveal"; | ||
import {isIntersecting} from "./lazyload.intersectionObserver"; | ||
import {runningOnBrowser, supportsIntersectionObserver} from "./lazyload.environment"; | ||
import { | ||
isIntersecting, | ||
getObserverSettings | ||
} from "./lazyload.intersectionObserver"; | ||
import { | ||
runningOnBrowser, | ||
supportsIntersectionObserver | ||
} from "./lazyload.environment"; | ||
const LazyLoad = function (customSettings, elements) { | ||
this._settings = getInstanceSettings(customSettings); | ||
this._setObserver(); | ||
this.update(elements); | ||
const LazyLoad = function(customSettings, elements) { | ||
this._settings = getInstanceSettings(customSettings); | ||
this._setObserver(); | ||
this.update(elements); | ||
}; | ||
LazyLoad.prototype = { | ||
_setObserver: function () { | ||
if (!supportsIntersectionObserver) { | ||
return; | ||
} | ||
_setObserver: function() { | ||
if (!supportsIntersectionObserver) { | ||
return; | ||
} | ||
const revealIntersectingElements = entries => { | ||
entries.forEach(entry => { | ||
if (isIntersecting(entry)) { | ||
let element = entry.target; | ||
this.load(element); | ||
this._observer.unobserve(element); | ||
} | ||
}); | ||
this._elements = purgeElements(this._elements); | ||
}; | ||
this._observer = new IntersectionObserver( | ||
revealIntersectingElements, | ||
getObserverSettings(this._settings) | ||
); | ||
}, | ||
const settings = this._settings; | ||
const observerSettings = { | ||
root: settings.container === document ? null : settings.container, | ||
rootMargin: settings.threshold + "px" | ||
}; | ||
const revealIntersectingElements = (entries) => { | ||
entries.forEach(entry => { | ||
if (isIntersecting(entry)) { | ||
let element = entry.target; | ||
revealElement(element, this._settings); | ||
this._observer.unobserve(element); | ||
} | ||
}); | ||
this._elements = purgeElements(this._elements); | ||
}; | ||
this._observer = new IntersectionObserver(revealIntersectingElements, observerSettings); | ||
}, | ||
loadAll: function() { | ||
this._elements.forEach(element => { | ||
this.load(element); | ||
}); | ||
this._elements = purgeElements(this._elements); | ||
}, | ||
loadAll: function() { | ||
const settings = this._settings; | ||
// Fallback: load all elements at once | ||
this._elements.forEach(element => { | ||
revealElement(element, settings); | ||
}); | ||
this._elements = purgeElements(this._elements); | ||
}, | ||
update: function(elements) { | ||
const settings = this._settings; | ||
const nodeSet = | ||
elements || | ||
settings.container.querySelectorAll(settings.elements_selector); | ||
update: function (elements) { | ||
const settings = this._settings; | ||
const nodeSet = elements || settings.container.querySelectorAll(settings.elements_selector); | ||
this._elements = purgeElements(Array.prototype.slice.call(nodeSet)); // nodeset to array for IE compatibility | ||
if (this._observer) { | ||
this._elements.forEach(element => { | ||
this._observer.observe(element); | ||
}); | ||
return; | ||
} | ||
// Fallback: load all elements at once | ||
this.loadAll(); | ||
}, | ||
this._elements = purgeElements(Array.prototype.slice.call(nodeSet)); // nodeset to array for IE compatibility | ||
if (this._observer) { | ||
this._elements.forEach(element => { | ||
this._observer.observe(element); | ||
}); | ||
return; | ||
} | ||
this.loadAll(); | ||
}, | ||
destroy: function() { | ||
if (this._observer) { | ||
purgeElements(this._elements).forEach(element => { | ||
this._observer.unobserve(element); | ||
}); | ||
this._observer = null; | ||
} | ||
this._elements = null; | ||
this._settings = null; | ||
}, | ||
destroy: function () { | ||
if (this._observer) { | ||
purgeElements(this._elements).forEach(element => { | ||
this._observer.unobserve(element); | ||
}); | ||
this._observer = null; | ||
} | ||
this._elements = null; | ||
this._settings = null; | ||
} | ||
} | ||
load: function(element, force) { | ||
revealElement(element, this._settings, force); | ||
} | ||
}; | ||
@@ -77,5 +85,5 @@ /* Automatic instances creation if required (useful for async script loading!) */ | ||
if (runningOnBrowser && autoInitOptions) { | ||
autoInitialize(LazyLoad, autoInitOptions); | ||
autoInitialize(LazyLoad, autoInitOptions); | ||
} | ||
export default LazyLoad; | ||
export default LazyLoad; |
@@ -1,7 +0,5 @@ | ||
import {getData} from "./lazyload.data"; | ||
import { getWasProcessed } from "./lazyload.data"; | ||
export default function (elements) { | ||
return elements.filter((element) => { | ||
return !getData(element, "was-processed"); | ||
}); | ||
} | ||
export default function(elements) { | ||
return elements.filter(element => !getWasProcessed(element)); | ||
} |
@@ -1,9 +0,9 @@ | ||
import {setSources} from "./lazyload.setSources"; | ||
import {setData} from "./lazyload.data"; | ||
import {addClass, removeClass} from "./lazyload.class"; | ||
import { setSources } from "./lazyload.setSources"; | ||
import { getWasProcessed, setWasProcessed } from "./lazyload.data"; | ||
import { addClass, removeClass } from "./lazyload.class"; | ||
const callCallback = function (callback, argument) { | ||
if (callback) { | ||
callback(argument); | ||
} | ||
const callCallback = function(callback, argument) { | ||
if (callback) { | ||
callback(argument); | ||
} | ||
}; | ||
@@ -15,35 +15,41 @@ | ||
const removeListeners = function(element, loadHandler, errorHandler) { | ||
element.removeEventListener(loadString, loadHandler); | ||
element.removeEventListener(errorString, errorHandler); | ||
element.removeEventListener(loadString, loadHandler); | ||
element.removeEventListener(errorString, errorHandler); | ||
}; | ||
const addOneShotListeners = function(element, settings) { | ||
const onLoad = (event) => { | ||
onEvent(event, true, settings); | ||
removeListeners(element, onLoad, onError); | ||
} | ||
const onError = (event) => { | ||
onEvent(event, false, settings); | ||
removeListeners(element, onLoad, onError); | ||
} | ||
element.addEventListener(loadString, onLoad); | ||
element.addEventListener(errorString, onError); | ||
const onLoad = event => { | ||
onEvent(event, true, settings); | ||
removeListeners(element, onLoad, onError); | ||
}; | ||
const onError = event => { | ||
onEvent(event, false, settings); | ||
removeListeners(element, onLoad, onError); | ||
}; | ||
element.addEventListener(loadString, onLoad); | ||
element.addEventListener(errorString, onError); | ||
}; | ||
const onEvent = function (event, success, settings) { | ||
const element = event.target; | ||
removeClass(element, settings.class_loading); | ||
addClass(element, (success ? settings.class_loaded : settings.class_error)); // Setting loaded or error class | ||
callCallback(success ? settings.callback_load : settings.callback_error, element); // Calling loaded or error callback | ||
const onEvent = function(event, success, settings) { | ||
const element = event.target; | ||
removeClass(element, settings.class_loading); | ||
addClass(element, success ? settings.class_loaded : settings.class_error); // Setting loaded or error class | ||
callCallback( | ||
success ? settings.callback_load : settings.callback_error, | ||
element | ||
); | ||
}; | ||
export default function(element, settings, force) { | ||
if (!force && getWasProcessed(element)) { | ||
return; // element has already been processed and force wasn't true | ||
} | ||
callCallback(settings.callback_enter, element); | ||
if (["IMG", "IFRAME", "VIDEO"].indexOf(element.tagName) > -1) { | ||
addOneShotListeners(element, settings); | ||
addClass(element, settings.class_loading); | ||
} | ||
setSources(element, settings); | ||
setWasProcessed(element); | ||
callCallback(settings.callback_set, element); | ||
} | ||
export default function (element, settings) { | ||
callCallback(settings.callback_enter, element); | ||
if (["IMG", "IFRAME", "VIDEO"].indexOf(element.tagName) > -1) { | ||
addOneShotListeners(element, settings); | ||
addClass(element, settings.class_loading); | ||
} | ||
setSources(element, settings); | ||
setData(element, "was-processed", true); | ||
callCallback(settings.callback_set, element); | ||
}; |
@@ -32,27 +32,27 @@ import { getData } from "./lazyload.data"; | ||
const srcDataValue = getData(element, srcDataName); | ||
const tagName = element.tagName; | ||
if (tagName === "IMG") { | ||
const parent = element.parentNode; | ||
if (parent && parent.tagName === "PICTURE") { | ||
setSourcesInChildren(parent, "srcset", srcsetDataName); | ||
switch (element.tagName) { | ||
case "IMG": { | ||
const parent = element.parentNode; | ||
if (parent && parent.tagName === "PICTURE") { | ||
setSourcesInChildren(parent, "srcset", srcsetDataName); | ||
} | ||
const sizesDataValue = getData(element, sizesDataName); | ||
setAttributeIfNotNullOrEmpty(element, "sizes", sizesDataValue); | ||
const srcsetDataValue = getData(element, srcsetDataName); | ||
setAttributeIfNotNullOrEmpty(element, "srcset", srcsetDataValue); | ||
setAttributeIfNotNullOrEmpty(element, "src", srcDataValue); | ||
break; | ||
} | ||
const sizesDataValue = getData(element, sizesDataName); | ||
setAttributeIfNotNullOrEmpty(element, "sizes", sizesDataValue); | ||
const srcsetDataValue = getData(element, srcsetDataName); | ||
setAttributeIfNotNullOrEmpty(element, "srcset", srcsetDataValue); | ||
setAttributeIfNotNullOrEmpty(element, "src", srcDataValue); | ||
return; | ||
case "IFRAME": | ||
setAttributeIfNotNullOrEmpty(element, "src", srcDataValue); | ||
break; | ||
case "VIDEO": | ||
setSourcesInChildren(element, "src", srcDataName); | ||
setAttributeIfNotNullOrEmpty(element, "src", srcDataValue); | ||
break; | ||
default: | ||
if (srcDataValue) { | ||
element.style.backgroundImage = `url("${srcDataValue}")`; | ||
} | ||
} | ||
if (tagName === "IFRAME") { | ||
setAttributeIfNotNullOrEmpty(element, "src", srcDataValue); | ||
return; | ||
} | ||
if (tagName === "VIDEO") { | ||
setSourcesInChildren(element, "src", srcDataName); | ||
setAttributeIfNotNullOrEmpty(element, "src", srcDataValue); | ||
return; | ||
} | ||
if (srcDataValue) { | ||
element.style.backgroundImage = `url("${srcDataValue}")`; | ||
} | ||
}; |
10
todo.md
TODO | ||
==== | ||
* Missing documenation | ||
* Constructor takes a nodeset as 2nd parameter, see 10.2 release | ||
* `update()` takes with the NodeSet object - see 10.2 release | ||
* Remove that Array.prototype.slice.call and use Array.from on the elements nodeset? | ||
* PurgeElements can be avoided if we `querySelectorAll` with a "not `[data-was-processed]`" selector? | ||
* Test more modules | ||
* autoinitialize | ||
* purge | ||
* reveal | ||
* reveal | ||
* revealElement (breaking change) | ||
* now that .load() and .loadAll() now exist, in the `reveal` function we should have another callback to notify "start loading" and `callback_enter` should be moved outside of the `reveal` function, just to notify that the element entered the viewport -- BREAKING CHANGE! | ||
* check why on load.html the "loaded" callback is called twice in some cases |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
952840
138
1179
612