Comparing version 3.0.0 to 3.1.0
'use strict'; | ||
function yall (options) { | ||
if (!options) { | ||
options = {}; | ||
} | ||
options = options || {}; | ||
const intersectionObserverSupport = "IntersectionObserver" in window && "IntersectionObserverEntry" in window; | ||
const lazyClass = options.lazyClass || "lazy"; | ||
const lazyBackgroundClass = options.lazyBackgroundClass || "lazy-bg"; | ||
const idleLoadTimeout = "idleLoadTimeout" in options ? options.idleLoadTimeout : 100; | ||
const idleLoadTimeout = "idleLoadTimeout" in options ? options.idleLoadTimeout : 200; | ||
const observeChanges = options.observeChanges || false; | ||
const selectorString = `img.${lazyClass},video.${lazyClass},iframe.${lazyClass},.${lazyBackgroundClass}`; | ||
const events = options.events || {}; | ||
// This abstraction shaves off a few bytes (plus it's nifty). | ||
const sliceCall = arr => [].slice.call(arr); | ||
const nodeListToArray = nodeList => [].slice.call(nodeList); | ||
@@ -24,7 +22,7 @@ // This function handles lazy loading of elements. | ||
if (parentNode.nodeName == "PICTURE") { | ||
sourceElements = sliceCall(parentNode.querySelectorAll("source")); | ||
sourceElements = nodeListToArray(parentNode.querySelectorAll("source")); | ||
} | ||
if (element.nodeName == "VIDEO") { | ||
sourceElements = sliceCall(element.querySelectorAll("source")); | ||
sourceElements = nodeListToArray(element.querySelectorAll("source")); | ||
} | ||
@@ -49,2 +47,10 @@ | ||
const yallBind = (intersectionListener, element) => { | ||
intersectionListener.observe(element); | ||
Object.keys(events).forEach(eventKey => { | ||
element.addEventListener(eventKey, events[eventKey].listener || events[eventKey], events[eventKey].options || undefined); | ||
}); | ||
}; | ||
// Added because there was a number of patterns like this peppered throughout | ||
@@ -60,3 +66,3 @@ // the code. This just flips necessary data- attrs on an element | ||
let lazyElements = sliceCall(document.querySelectorAll(selectorString)); | ||
let lazyElements = nodeListToArray(document.querySelectorAll(selectorString)); | ||
@@ -73,3 +79,3 @@ // If the current user agent is a known crawler, immediately load all media | ||
if (intersectionObserverSupport) { | ||
if ("IntersectionObserver" in window && "IntersectionObserverEntry" in window) { | ||
var intersectionListener = new IntersectionObserver((entries, observer) => { | ||
@@ -104,18 +110,18 @@ entries.forEach(entry => { | ||
for (let lazyElementIndex in lazyElements) { | ||
intersectionListener.observe(lazyElements[lazyElementIndex]); | ||
yallBind(intersectionListener, lazyElements[lazyElementIndex]); | ||
} | ||
} | ||
if ("MutationObserver" in window && observeChanges) { | ||
new MutationObserver(() => { | ||
sliceCall(document.querySelectorAll(selectorString)).forEach(newElement => { | ||
if (lazyElements.indexOf(newElement) < 0 && intersectionObserverSupport) { | ||
lazyElements.push(newElement); | ||
intersectionListener.observe(newElement); | ||
} | ||
if ("MutationObserver" in window && observeChanges) { | ||
new MutationObserver(() => { | ||
nodeListToArray(document.querySelectorAll(selectorString)).forEach(newElement => { | ||
if (lazyElements.indexOf(newElement) < 0) { | ||
lazyElements.push(newElement); | ||
yallBind(intersectionListener, newElement); | ||
} | ||
}); | ||
}).observe(document.querySelector(options.observeRootSelector || "body"), options.mutationObserverOptions || { | ||
childList: true, | ||
subtree: true | ||
}); | ||
}).observe(document.querySelector(options.observeRootSelector || "body"), options.mutationObserverOptions || { | ||
childList: true, | ||
subtree: true | ||
}); | ||
} | ||
} | ||
@@ -122,0 +128,0 @@ } |
@@ -1,2 +0,2 @@ | ||
/*yall.js 3.0.0*/ | ||
var yall=function(){"use strict";return function(e){e||(e={});var n="IntersectionObserver"in window&&"IntersectionObserverEntry"in window,o=e.lazyClass||"lazy",r=e.lazyBackgroundClass||"lazy-bg",t="idleLoadTimeout"in e?e.idleLoadTimeout:100,i=e.observeChanges||!1,s="img."+o+",video."+o+",iframe."+o+",."+r,a=function(e){return[].slice.call(e)},c=function(n){var o,t=n.parentNode;for(var i in"PICTURE"==t.nodeName&&(o=a(t.querySelectorAll("source"))),"VIDEO"==n.nodeName&&(o=a(n.querySelectorAll("source"))),o)l(o[i]);l(n),n.autoplay&&n.load(),n.classList.contains(r)&&(n.classList.remove(r),n.classList.add(e.lazyBackgroundLoaded||"lazy-bg-loaded"))},l=function(e){["srcset","src","poster"].forEach(function(n){n in e.dataset&&(e[n]=e.dataset[n])})},u=a(document.querySelectorAll(s));if(/(google|bing|yandex|duckduck)bot/i.test(navigator.userAgent))for(var d in u)c(u[d]);else{if(n){var f=new IntersectionObserver(function(e,n){e.forEach(function(e){if(e.isIntersecting){var r=e.target;"requestIdleCallback"in window&&t?requestIdleCallback(function(){c(r)},{timeout:t}):c(r),r.classList.remove(o),n.unobserve(r),(u=u.filter(function(e){return e!=r})).length||i||f.disconnect()}})},{rootMargin:("threshold"in e?e.threshold:200)+"px 0%"});for(var v in u)f.observe(u[v])}"MutationObserver"in window&&i&&new MutationObserver(function(){a(document.querySelectorAll(s)).forEach(function(e){u.indexOf(e)<0&&n&&(u.push(e),f.observe(e))})}).observe(document.querySelector(e.observeRootSelector||"body"),e.mutationObserverOptions||{childList:!0,subtree:!0})}}}(); | ||
/*yall.js 3.1.0*/ | ||
var yall=function(){"use strict";return function(e){var n=(e=e||{}).lazyClass||"lazy",t=e.lazyBackgroundClass||"lazy-bg",o="idleLoadTimeout"in e?e.idleLoadTimeout:200,r=e.observeChanges||!1,i="img."+n+",video."+n+",iframe."+n+",."+t,s=e.events||{},a=function(e){return[].slice.call(e)},c=function(n){var o,r=n.parentNode;for(var i in"PICTURE"==r.nodeName&&(o=a(r.querySelectorAll("source"))),"VIDEO"==n.nodeName&&(o=a(n.querySelectorAll("source"))),o)u(o[i]);u(n),n.autoplay&&n.load(),n.classList.contains(t)&&(n.classList.remove(t),n.classList.add(e.lazyBackgroundLoaded||"lazy-bg-loaded"))},l=function(e,n){e.observe(n),Object.keys(s).forEach(function(e){n.addEventListener(e,s[e].listener||s[e],s[e].options||void 0)})},u=function(e){["srcset","src","poster"].forEach(function(n){n in e.dataset&&(e[n]=e.dataset[n])})},d=a(document.querySelectorAll(i));if(/(google|bing|yandex|duckduck)bot/i.test(navigator.userAgent))for(var f in d)c(d[f]);else if("IntersectionObserver"in window&&"IntersectionObserverEntry"in window){var v=new IntersectionObserver(function(e,t){e.forEach(function(e){if(e.isIntersecting){var i=e.target;"requestIdleCallback"in window&&o?requestIdleCallback(function(){c(i)},{timeout:o}):c(i),i.classList.remove(n),t.unobserve(i),(d=d.filter(function(e){return e!=i})).length||r||v.disconnect()}})},{rootMargin:("threshold"in e?e.threshold:200)+"px 0%"});for(var b in d)l(v,d[b]);"MutationObserver"in window&&r&&new MutationObserver(function(){a(document.querySelectorAll(i)).forEach(function(e){d.indexOf(e)<0&&(d.push(e),l(v,e))})}).observe(document.querySelector(e.observeRootSelector||"body"),e.mutationObserverOptions||{childList:!0,subtree:!0})}}}(); |
{ | ||
"name": "yall-js", | ||
"version": "3.0.0", | ||
"version": "3.1.0", | ||
"description": "Yet Another Lazy Loader", | ||
@@ -5,0 +5,0 @@ "main": "./dist/yall.js", |
@@ -223,3 +223,3 @@ # yall.js (Yet Another Lazy Loader) | ||
**default:** `"lazy"` | ||
**default:** `"lazy"`<br> | ||
The element class used by yall.js to find elements to lazy load. Change this is if a `class` attribute value of `lazy` conflicts with your application. | ||
@@ -229,3 +229,3 @@ | ||
**default:** `"lazy-bg"` | ||
**default:** `"lazy-bg"`<br> | ||
The element class used by yall.js to find elements to lazy load CSS background images for. Change this if you'd prefer not to use the default. | ||
@@ -235,3 +235,3 @@ | ||
**default:** `"lazy-bg-loaded"` | ||
**default:** `"lazy-bg-loaded"`<br> | ||
When yall.js finds elements using the class specified by `lazyBackgroundClass`, it will remove that class and put this one in its place. This will be the class you use in your CSS to bring in your background image when the affected element is in the viewport. | ||
@@ -241,3 +241,3 @@ | ||
**default:** `100` | ||
**default:** `200`<br> | ||
In environments where `requestIdleCallback` is available, this option sets a deadline in milliseconds for `requestIdleCallback` to kick off lazy loading for an element. If this option is set to `0`, `requestIdleCallback` is never called, and lazy loading for the affected element(s) will begin immediately once they're in the viewport. | ||
@@ -247,8 +247,49 @@ | ||
**default:** `200` | ||
**default:** `200`<br> | ||
The threshold (in pixels) for how far elements need to be within the viewport to begin lazy loading. | ||
### `events` | ||
**default:** `{}`<br> | ||
An object of events that get sent directly to [`addEventListener`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener) for each element to be lazy loaded. Rather than building some opinionated, bespoke event management system, this system gets out of your way and allows you to specify whatever events you are possible to bind with `addEventListener`. Here's an example below: | ||
```javascript | ||
document.addEventListener("DOMContentLoaded", function () { | ||
yall({ | ||
events: { | ||
// The object key is sent as the first argument to `addEventListener`, | ||
// which is the event. The corresponding value can be the callback if you | ||
// don't want to send any options to `addEventListener`. | ||
load: function (event) { | ||
if (!event.target.classList.contains("lazy") && event.target.nodeName == "IMG") { | ||
event.target.classList.add("yall-loaded"); | ||
} | ||
}, | ||
// If we want to pass options to the third argument in `addEventListener`, | ||
// we can use a nested object syntax like so: | ||
error: { | ||
// Here, the `listener` member is the callback. | ||
listener: function (event) { | ||
if (!event.target.classList.contains("lazy") && event.target.nodeName == "IMG") { | ||
event.target.classList.add("yall-error"); | ||
} | ||
}, | ||
// The option below is sent as the third argument to `addEventListener`, | ||
// offering more control over how events are bound. If you want to | ||
// specify `useCapture` in lieu of options pass a boolean here instead. | ||
options: { | ||
once: true | ||
} | ||
} | ||
} | ||
}); | ||
}); | ||
``` | ||
Events for yall are bound at initialization time. This means that some events could fire multiple times. For instance, in the above `load` event example you can see that we check for the default class of `"lazy"` on the element. This is because the `load` event could fire when the initial image placeholder loaded (if one is specified) _and_ when the final image is lazy loaded. | ||
The advantage of this approach is that you can do pretty much anything you want in any of the events on the elements yall observes. The disadvantage is that it places the responsibility squarely on you to manage events. This feature is also not finalized, so there are chances you may find some unexpected behavior or breaking API changes in the future. | ||
### `observeChanges` | ||
**default:** `false` | ||
**default:** `false`<br> | ||
Use a Mutation Observer to examine the DOM for changes. This is useful if you're using yall.js in a single page application and want to lazy load resources for markup injected into the page after initial page render. _**Note:** This option is ignored if set to `true` in a browser that doesn't support Mutation Observer!_ | ||
@@ -258,3 +299,3 @@ | ||
**default:** `"body"` | ||
**default:** `"body"`<br> | ||
If `observeChanges` is set to `true`, the value of this string is fed into `document.querySelector` to limit the scope in which the Mutation Observer looks for DOM changes. The `<body>` element is used by default, but you can confine the observer to any valid CSS selector (e.g., `#main-wrapper`). | ||
@@ -264,3 +305,3 @@ | ||
**default:** `{ childList: true, subtree: true }` | ||
**default:** `{ childList: true, subtree: true }`<br> | ||
Options to pass to the `MutationObserver` instance. Read [this MDN guide](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver#MutationObserverInit) for a list of options. | ||
@@ -267,0 +308,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
31573
217
320