Comparing version 2.3.1 to 3.0.0
144
dist/yall.js
@@ -8,59 +8,33 @@ 'use strict'; | ||
const intersectionObserverSupport = "IntersectionObserver" in window && "IntersectionObserverEntry" in window && "intersectionRatio" in window.IntersectionObserverEntry.prototype; | ||
const idleCallbackSupport = "requestIdleCallback" in window; | ||
const eventsToBind = [ | ||
[document, "scroll"], | ||
[document, "touchmove"], | ||
[window, "resize"], | ||
[window, "orientationchange"] | ||
]; | ||
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 threshold = "threshold" in options ? options.threshold : 200; | ||
const observeChanges = options.observeChanges || false; | ||
const selectorString = `img.${lazyClass},video.${lazyClass},iframe.${lazyClass},.${lazyBackgroundClass}`; | ||
const idleCallbackOptions = { | ||
timeout: idleLoadTimeout | ||
}; | ||
// This abstraction shaves off a few bytes (plus it's nifty). | ||
const sliceCall = arr => [].slice.call(arr); | ||
// This function handles the lazy loading of elements. It's kicked off by the | ||
// scroll handlers/intersection observers further down. | ||
// This function handles lazy loading of elements. | ||
const yallLoad = element => { | ||
switch (element.nodeName) { | ||
case "IMG": | ||
let parentElement = element.parentNode; | ||
const parentNode = element.parentNode; | ||
let sourceElements; | ||
// Is the parent element a <picture>? | ||
if (parentElement.nodeName == "PICTURE") { | ||
sliceCall(parentElement.querySelectorAll("source")).forEach(source => { | ||
yallFlipDataAttrs(source); | ||
}); | ||
} | ||
if (parentNode.nodeName == "PICTURE") { | ||
sourceElements = sliceCall(parentNode.querySelectorAll("source")); | ||
} | ||
yallFlipDataAttrs(element); | ||
if (element.nodeName == "VIDEO") { | ||
sourceElements = sliceCall(element.querySelectorAll("source")); | ||
} | ||
break; | ||
for (let sourceElementIndex in sourceElements) { | ||
yallFlipDataAttrs(sourceElements[sourceElementIndex]); | ||
} | ||
case "VIDEO": | ||
sliceCall(element.querySelectorAll("source")).forEach(source => { | ||
yallFlipDataAttrs(source); | ||
}); | ||
yallFlipDataAttrs(element); | ||
// We didn't need this before, but with the addition of lazy loading | ||
// `poster` images, we need to run the flip attributes function on the | ||
// video element itself so we can trigger lazy loading behavior on those. | ||
yallFlipDataAttrs(element); | ||
if (element.autoplay) { | ||
element.load(); | ||
} | ||
break; | ||
case "IFRAME": | ||
yallFlipDataAttrs(element); | ||
break; | ||
if (element.autoplay) { | ||
element.load(); | ||
} | ||
@@ -85,37 +59,2 @@ | ||
// When intersection observer is unavailable, this function is bound to scroll | ||
// (and other) event handlers to load images the "old" way. | ||
const yallBack = () => { | ||
let active = false; | ||
if (!active && lazyElements.length) { | ||
active = true; | ||
setTimeout(() => { | ||
lazyElements.forEach(lazyElement => { | ||
if (lazyElement.getBoundingClientRect().top <= (window.innerHeight + threshold) && lazyElement.getBoundingClientRect().bottom >= -threshold && getComputedStyle(lazyElement).display != "none") { | ||
if (idleCallbackSupport && idleLoadTimeout) { | ||
requestIdleCallback(() => { | ||
yallLoad(lazyElement); | ||
}, idleCallbackOptions); | ||
} else { | ||
yallLoad(lazyElement); | ||
} | ||
lazyElement.classList.remove(lazyClass); | ||
lazyElements = lazyElements.filter(element => element != lazyElement); | ||
} | ||
}); | ||
active = false; | ||
if (!lazyElements.length && !observeChanges) { | ||
eventsToBind.forEach(eventPair => { | ||
eventPair[0].removeEventListener(eventPair[1], yallBack); | ||
}); | ||
} | ||
}, "throttleTime" in options ? options.throttleTime : 200); | ||
} | ||
}; | ||
let lazyElements = sliceCall(document.querySelectorAll(selectorString)); | ||
@@ -126,5 +65,5 @@ | ||
if (/(google|bing|yandex|duckduck)bot/i.test(navigator.userAgent)) { | ||
lazyElements.forEach(lazyElement => { | ||
yallLoad(lazyElement); | ||
}); | ||
for (let lazyElementIndex in lazyElements) { | ||
yallLoad(lazyElements[lazyElementIndex]); | ||
} | ||
@@ -138,8 +77,10 @@ return; | ||
if (entry.isIntersecting) { | ||
let element = entry.target; | ||
const element = entry.target; | ||
if (idleCallbackSupport && idleLoadTimeout) { | ||
if ("requestIdleCallback" in window && idleLoadTimeout) { | ||
requestIdleCallback(() => { | ||
yallLoad(element); | ||
}, idleCallbackOptions); | ||
}, { | ||
timeout: idleLoadTimeout | ||
}); | ||
} else { | ||
@@ -159,30 +100,17 @@ yallLoad(element); | ||
}, { | ||
rootMargin: `${threshold}px 0%` | ||
rootMargin: `${"threshold" in options ? options.threshold : 200}px 0%` | ||
}); | ||
lazyElements.forEach(lazyElement => { | ||
intersectionListener.observe(lazyElement); | ||
}); | ||
} else { | ||
eventsToBind.forEach(eventPair => { | ||
eventPair[0].addEventListener(eventPair[1], yallBack); | ||
}); | ||
yallBack(); | ||
for (let lazyElementIndex in lazyElements) { | ||
intersectionListener.observe(lazyElements[lazyElementIndex]); | ||
} | ||
} | ||
if ("MutationObserver" in window && observeChanges) { | ||
new MutationObserver(mutations => { | ||
mutations.forEach(() => { | ||
sliceCall(document.querySelectorAll(selectorString)).forEach(newElement => { | ||
if (lazyElements.indexOf(newElement) == -1) { | ||
lazyElements.push(newElement); | ||
if (intersectionObserverSupport) { | ||
intersectionListener.observe(newElement); | ||
} else { | ||
yallBack(); | ||
} | ||
} | ||
}); | ||
new MutationObserver(() => { | ||
sliceCall(document.querySelectorAll(selectorString)).forEach(newElement => { | ||
if (lazyElements.indexOf(newElement) < 0 && intersectionObserverSupport) { | ||
lazyElements.push(newElement); | ||
intersectionListener.observe(newElement); | ||
} | ||
}); | ||
@@ -189,0 +117,0 @@ }).observe(document.querySelector(options.observeRootSelector || "body"), options.mutationObserverOptions || { |
@@ -1,2 +0,2 @@ | ||
/*yall.js 2.3.1*/ | ||
var yall=function(){"use strict";return function(e){e||(e={});var t="IntersectionObserver"in window&&"IntersectionObserverEntry"in window&&"intersectionRatio"in window.IntersectionObserverEntry.prototype,n="requestIdleCallback"in window,o=[[document,"scroll"],[document,"touchmove"],[window,"resize"],[window,"orientationchange"]],r=e.lazyClass||"lazy",i=e.lazyBackgroundClass||"lazy-bg",c="idleLoadTimeout"in e?e.idleLoadTimeout:100,a="threshold"in e?e.threshold:200,s=e.observeChanges||!1,l="img."+r+",video."+r+",iframe."+r+",."+i,u={timeout:c},d=function(e){return[].slice.call(e)},f=function(t){switch(t.nodeName){case"IMG":var n=t.parentNode;"PICTURE"==n.nodeName&&d(n.querySelectorAll("source")).forEach(function(e){v(e)}),v(t);break;case"VIDEO":d(t.querySelectorAll("source")).forEach(function(e){v(e)}),v(t),t.autoplay&&t.load();break;case"IFRAME":v(t)}t.classList.contains(i)&&(t.classList.remove(i),t.classList.add(e.lazyBackgroundLoaded||"lazy-bg-loaded"))},v=function(e){["srcset","src","poster"].forEach(function(t){t in e.dataset&&(e[t]=e.dataset[t])})},h=function yallBack(){var t=!1;!t&&b.length&&(t=!0,setTimeout(function(){b.forEach(function(e){e.getBoundingClientRect().top<=window.innerHeight+a&&e.getBoundingClientRect().bottom>=-a&&"none"!=getComputedStyle(e).display&&(n&&c?requestIdleCallback(function(){f(e)},u):f(e),e.classList.remove(r),b=b.filter(function(t){return t!=e}))}),t=!1,b.length||s||o.forEach(function(e){e[0].removeEventListener(e[1],yallBack)})},"throttleTime"in e?e.throttleTime:200))},b=d(document.querySelectorAll(l));if(/(google|bing|yandex|duckduck)bot/i.test(navigator.userAgent))b.forEach(function(e){f(e)});else{if(t){var g=new IntersectionObserver(function(e,t){e.forEach(function(e){if(e.isIntersecting){var o=e.target;n&&c?requestIdleCallback(function(){f(o)},u):f(o),o.classList.remove(r),t.unobserve(o),(b=b.filter(function(e){return e!=o})).length||s||g.disconnect()}})},{rootMargin:a+"px 0%"});b.forEach(function(e){g.observe(e)})}else o.forEach(function(e){e[0].addEventListener(e[1],h)}),h();"MutationObserver"in window&&s&&new MutationObserver(function(e){e.forEach(function(){d(document.querySelectorAll(l)).forEach(function(e){-1==b.indexOf(e)&&(b.push(e),t?g.observe(e):h())})})}).observe(document.querySelector(e.observeRootSelector||"body"),e.mutationObserverOptions||{childList:!0,subtree:!0})}}}(); | ||
/*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})}}}(); |
{ | ||
"name": "yall-js", | ||
"version": "2.3.1", | ||
"version": "3.0.0", | ||
"description": "Yet Another Lazy Loader", | ||
@@ -12,3 +12,4 @@ "main": "./dist/yall.js", | ||
"test": "npm run lint && node http.js", | ||
"build": "npm run clean && npx rollup -c" | ||
"build": "npm run clean && npx rollup -c", | ||
"postinstall": "node -e \"console.log('Thank you for trying out version 3 of yall.js. If you find bugs, please file an issue with appropriate details at https://github.com/malchata/yall.js/issues')\"" | ||
}, | ||
@@ -35,8 +36,8 @@ "repository": { | ||
"@babel/cli": "^7.2.3", | ||
"@babel/core": "^7.3.4", | ||
"@babel/preset-env": "^7.3.4", | ||
"@babel/core": "^7.4.0", | ||
"@babel/preset-env": "^7.4.2", | ||
"eslint": "^5.15.3", | ||
"express": "^4.16.4", | ||
"rimraf": "^2.6.3", | ||
"rollup": "^1.6.0", | ||
"rollup": "^1.7.0", | ||
"rollup-plugin-babel": "^4.3.2", | ||
@@ -43,0 +44,0 @@ "rollup-plugin-copy": "^0.2.3", |
108
README.md
@@ -7,3 +7,3 @@ # yall.js (Yet Another Lazy Loader) | ||
<p align="center"> | ||
<img src="https://img.badgesize.io/malchata/yall.js/master/dist/yall.min.js?label=Uncompressed" alt="Uncompressed size."> <img src="https://img.badgesize.io/malchata/yall.js/master/dist/yall.min.js?compression=gzip&label=gzip" alt="gzip size."> <img src="https://img.badgesize.io/malchata/yall.js/master/dist/yall.min.js?compression=brotli&label=brotli" alt="Brotli size."> | ||
<img src="https://img.badgesize.io/malchata/yall.js/asfaloth/dist/yall.min.js?label=Uncompressed" alt="Uncompressed size."> <img src="https://img.badgesize.io/malchata/yall.js/asfaloth/dist/yall.min.js?compression=gzip&label=gzip" alt="gzip size."> <img src="https://img.badgesize.io/malchata/yall.js/asfaloth/dist/yall.min.js?compression=brotli&label=brotli" alt="Brotli size."> | ||
</p> | ||
@@ -14,11 +14,11 @@ <p align="center"> | ||
<p align="center"> | ||
<img src="https://img.badgesize.io/malchata/yall.js/master/dist/yall.min.mjs?label=Uncompressed" alt="Uncompressed size."> <img src="https://img.badgesize.io/malchata/yall.js/master/dist/yall.min.mjs?compression=gzip&label=gzip" alt="gzip size."> <img src="https://img.badgesize.io/malchata/yall.js/master/dist/yall.min.mjs?compression=brotli&label=brotli" alt="Brotli size."> | ||
<img src="https://img.badgesize.io/malchata/yall.js/asfaloth/dist/yall.min.mjs?label=Uncompressed" alt="Uncompressed size."> <img src="https://img.badgesize.io/malchata/yall.js/asfaloth/dist/yall.min.mjs?compression=gzip&label=gzip" alt="gzip size."> <img src="https://img.badgesize.io/malchata/yall.js/asfaloth/dist/yall.min.mjs?compression=brotli&label=brotli" alt="Brotli size."> | ||
</p> | ||
yall.js is a featured-packed SEO-friendly lazy loader for `<img>`, `<picture>`, `<video>` and `<iframe>` elements, and can also lazy load CSS background images. It works in all modern browsers as well as IE11. It uses [Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) where available, but falls back to `scroll`, `touchmove`, `resize`, and `orientationchange` events where necessary. It can also monitor the DOM for changes using [Mutation Observer](https://hacks.mozilla.org/2012/05/dom-mutationobserver-reacting-to-dom-changes-without-killing-browser-performance/) to lazy load media elements that have been appended to the DOM after initial page render, which may be desirable for single page applications. It can also (optionally) optimize use of browser idle time using [`requestIdleCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback). | ||
yall.js is a featured-packed SEO-friendly lazy loader for `<img>`, `<picture>`, `<video>` and `<iframe>` elements, and can also lazy load CSS background images. It works in all modern browsers as well as IE11. It uses [Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) where available, but as of version 3, this API must be polyfilled for older browsers. It can also monitor the DOM for changes using [Mutation Observer](https://hacks.mozilla.org/2012/05/dom-mutationobserver-reacting-to-dom-changes-without-killing-browser-performance/) to lazy load media elements that have been appended to the DOM after initial page render, which may be desirable for single page applications. It can also optimize use of browser idle time using [`requestIdleCallback`](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback). | ||
While yall.js has only been offered in the past as a drop-in script, it's now installable as an npm package: | ||
To use yall, you can grab `yall.min.js` (or `yall.min.mjs` if you're the modern sort) from the `dist` directory and slap it on your page. You can also install version 3 with npm by pointing to the `next` tag: | ||
```shell | ||
npm install yall-js | ||
npm install yall-js@next | ||
``` | ||
@@ -28,3 +28,3 @@ | ||
This is version 2 of yall.js, and introduces breaking changes over version 1. While version 1 only required you to include the script and tag elements with a `class` of `lazy`, this script must be explicitly initialized: | ||
This is version 3 of yall.js, and introduces breaking changes over all prior versions. This is simplest way to initialize yall.js: | ||
@@ -45,4 +45,10 @@ ```javascript | ||
From there, lazy loading elements with yall.js depends on _what_ you want to lazy load. Let's take a look at what you can do with it. | ||
Unlike version 2 which provided a fallback for browsers without `IntersectionObserver` support, version 3 requires a polyfill. You can conditionally polyfill this feature through [polyfill.io](https://polyfill.io/) like so: | ||
```html | ||
<script src="https://polyfill.io/v2/polyfill.min.js?features=IntersectionObserver"></script> | ||
``` | ||
From there, lazy loading elements depends on _what_ you want to lazy load. Let's take a look at what you can do with it. | ||
### `<img>` | ||
@@ -57,3 +63,3 @@ | ||
In this case, we specify an optional placeholder image in the `src` attribute, and point to the image we want to lazy load in the `data-src` attribute. Attaching a `class` of `lazy` exposes elements to yall.js, and is necessary for the lazy loader to work (although this class value can be overridden via the API options). Let's look at an example using both `src` and `srcset`: | ||
Here, we specify an optional placeholder image in the `src` attribute, and point to the image we want to lazy load in the `data-src` attribute. Attaching a `class` of `lazy` exposes elements to yall.js, and is necessary for the lazy loader to work (although this class value can be overridden). Let's look at an example using both `src` and `srcset`: | ||
@@ -65,7 +71,7 @@ ```html | ||
**Note:** If you're using `media` and `sizes` atttributes on your elements, you don't need to prefix them with `data-`. Leave them as is and the browser will still choose the proper sources for the given viewport conditions. | ||
**Note:** If you're using `media` and `sizes` attributes, you don't need to prefix them with `data-`. Leave those attributes as is. | ||
### `<picture>` | ||
Since `<picture>` is a thing now, yall.js supports that, too: | ||
Since `<picture>` is a thing, yall.js supports that too: | ||
@@ -95,7 +101,7 @@ ```html | ||
The pattern is largely the same as it is with the `<picture>` use case, only the `lazy` class is applied to the `<video>` element. **Tip**: If you're embedding videos that _don't_ emulate animated GIFs (i.e., non autoplaying video), it's better to _not_ lazy load them. Instead, lean on the [`preload` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#attr-preload) to defer loading of video content. _**Please note that video autoplay policies may change at any time, meaning your video may not autoplay on some platforms!**_ | ||
The pattern is largely the same as it is with `<picture>`, only the `lazy` class is applied to the `<video>` element. **Tip:** If you're embedding videos that _don't_ emulate animated GIFs (i.e., non autoplaying video), it's better to _not_ lazy load them. Instead, lean on the [`preload` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#attr-preload) to defer loading of video content. _**Please note that video autoplay policies may change at any time, meaning your video may not autoplay on some platforms! Such behaviors are not bugs, but rather features designed to conserve the user's bandwidth and preferences. Filing issues related to video autoplay will likely be rejected.**_ | ||
#### Lazy-loading `poster` placeholder images for non-autoplaying video | ||
Sometimes you have video you'd rather not autoplay, such as when your videos have an audio track, or perhaps you want to be more considerate of your user's bandwidth. In cases like these, the [`poster` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#attr-poster) can be used to load a placeholder image. However, these images can also be rather large, especially if you have a number of videos on the page that use this pattern. As of version 2.2.0, you can now lazy load `poster` images with the following markup pattern: | ||
Sometimes you have video you'd rather not autoplay, such as those videos with an audio track. Or, perhaps you want to be more considerate of your user's bandwidth (how nice of you). In these cases, the [`poster` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#attr-poster) can be used to load a placeholder image. However, these images can also be rather large, especially if you have a number of videos on the page that use this pattern. As of version 2.2.0, you can now lazy load `poster` images with the following markup pattern: | ||
@@ -109,6 +115,10 @@ ```html | ||
This pattern is slightly different than the one above. Because we're not trying to replace animated GIFs, we've removed a number of attributes from the `video` tag that aren't necessary in this use case. Instead, we've done away with the usual `data-src` attribute, and specified `preload="none"` to ensure the browser doesn't preload any of the video. To lazy load the `poster` image itself, we specify the image to load in a `data-poster` attribute. The `controls` attribute is added in this case to give the reveal the browser's interface to control video playback. | ||
This pattern is slightly different than the one above. Because we're not trying to emulate animated GIFs, we've removed a number of attributes from the `video` tag that aren't necessary in this case: | ||
**Note:** For the sake of your users, don't mix the above markup patterns. If a video is going to use `autoplay` to replace an animated image, lazy loading a placeholder image via `data-poster` isn't necessary. | ||
1., We've done away with the usual `data-src` attribute, and specified `preload="none"` to ensure the browser doesn't preload any of the video (which, depending on the browser, is not guaranteed). | ||
2. To lazy load the `poster` image itself, we specify the image to load in a `data-poster` attribute. | ||
3. The `controls` attribute is added here to allow the user to control video playback. | ||
**Note:** For the sake of your users, don't mix the above markup patterns. If a video is going to use `autoplay` to replace an animated image, lazy loading a placeholder image via `data-poster` isn't necessary. In fact, it's potentially wasteful. Furthermore, if you're unsure of what to do, let the browser handle this stuff and don't use yall.js to manage loading of videos. | ||
### `<iframe>` | ||
@@ -124,13 +134,13 @@ | ||
Last, but not least, you can use yall.js to lazy load images referenced in CSS. This might be useful if you have some very large background images you'd like to defer. Using this feature requires using _both_ HTML and CSS. To start, let's say you have a `<div>` that loads a very large masthead image: | ||
Last, but not least, you can use yall.js to lazy load images referenced in CSS. This might be useful if you have a very large `background-image` you'd like to defer. Proper use of this feature requires _both_ HTML and CSS. To start, let's say you have a `<div>` that loads a very large masthead `background-image`: | ||
```html | ||
<!-- I bet this loads a giant stock photo... --> | ||
<!-- I bet this loads a giant stock photo! --> | ||
<div class="masthead lazy-bg"></div> | ||
``` | ||
The key here is the `lazy-bg` class, which is the class yall.js attaches to (and can be changed via the `lazyBackgroundClass` option). When yall.js sees elements with this class, it will remove said class and replace it with a class of `lazy-bg-loaded` (which can also be changed with the `lazyBackgroundLoaded` option). From here, it's up to you to author CSS that makes use of this class to swap the image in. That CSS might look like this: | ||
The key here is the `lazy-bg` class, which is a class yall.js looks for (and can be changed via the `lazyBackgroundClass` option). When yall.js sees elements with this class, it will remove that class and replace it with a class of `lazy-bg-loaded` (which can also be changed with the `lazyBackgroundLoaded` option). From here, it's up to you to author CSS that makes use of this class to swap the image in. Such CSS might look like this: | ||
```css | ||
/* Note the lack of a `background` in this style. */ | ||
/* Pre-lazy loading styles */ | ||
.masthead { | ||
@@ -148,7 +158,7 @@ background: #e6e6e6; /* A little placeholder color */ | ||
This works because unlike HTML which loads most resources immediately, CSS loads resources only if the current state of the document builds a CSSOM which includes them. That means if your document's style tree changes later on to request a background image, the browser will fetch it the moment the change is applied. Leaning on this behavior is more sensible than using a mess of `data-` attributes pointing to possible image candidates, which could potentially add a bunch of extra markup and introduce edge cases that are difficult to code for. | ||
This works because, unlike HTML which loads most resources immediately, CSS loads resources only if the current state of the document builds a CSSOM which includes them. That means if your document's style tree changes later on to request a background image, the browser will fetch it the moment the change is applied. Leaning on this behavior is more sensible than using a mess of `data-` attributes pointing to possible image candidates, which could potentially add a bunch of extra markup and introduce edge cases that are difficult to code for. | ||
## What about users without JavaScript? | ||
Slap on some `<noscript>` goodness: | ||
Slap on some `<noscript>`: | ||
@@ -193,3 +203,3 @@ ```html | ||
Then place a `no-js` class on the `<html>` element like so: | ||
Then place a `no-js` class on the `<html>` element: | ||
@@ -222,19 +232,49 @@ ```html | ||
- `lazyClass` (default: `"lazy"`): 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. | ||
- `lazyBackgroundClass` (default: `"lazy-bg"`): 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. | ||
- `lazyBackgroundLoaded` (default: `"lazy-bg-loaded"`): 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. | ||
- `throttleTime` (default: `200`): In cases where Intersection Observer isn't available, standard event handlers are used. `throttleTime` allows you to control how often the code within these event handlers fire in milliseconds. | ||
- `idleLoadTimeout` (default: `100`): 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. | ||
- `threshold` (default: `200`): The threshold (in pixels) for how far elements need to be within the viewport to begin lazy loading. | ||
- `observeChanges` (default: `false`): 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!_ | ||
- `observeRootSelector` (default: `"body"`): 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`). | ||
- `mutationObserverOptions` (default: `{childList: true}`): 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. | ||
### `lazyClass` | ||
**default:** `"lazy"` | ||
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. | ||
### `lazyBackgroundClass` | ||
**default:** `"lazy-bg"` | ||
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. | ||
### `lazyBackgroundLoaded` | ||
**default:** `"lazy-bg-loaded"` | ||
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. | ||
### `idleLoadTimeout` | ||
**default:** `100` | ||
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. | ||
### `threshold` | ||
**default:** `200` | ||
The threshold (in pixels) for how far elements need to be within the viewport to begin lazy loading. | ||
### `observeChanges` | ||
**default:** `false` | ||
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!_ | ||
### `observeRootSelector` | ||
**default:** `"body"` | ||
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`). | ||
### `mutationObserverOptions` | ||
**default:** `{ childList: true, subtree: true }` | ||
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. | ||
## Words of advice | ||
It is not yall.js's job to minimize layout shifting for you. Use appropriate `width` and `height` attributes on elements, as well as lightweight placeholders for your images. | ||
It is not this script's job to minimize layout shifting for you. Use appropriate `width` and `height` attributes, as well as lightweight placeholders for your images. | ||
In the case of `<video>`, avoid lazy loading a placeholder with the `data-poster` attribute for autoplaying videos and just use `poster`. On the other hand _do_ consider lazy loading a placeholder image with `data-poster` for non-autoplaying videos. | ||
In the case of `<video>`, avoid lazy loading a placeholder with the `data-poster` attribute for autoplaying videos and just use `poster`. On the other hand, _do_ consider lazy loading a placeholder image with `data-poster` for non-autoplaying videos. | ||
For general guidance, please check out the `test` folder to see how you might use placeholders with yall.js. If you don't want to bother with placeholders, you can omit them in all use cases. Yall.js will still work, albeit the experience may be weird and jumpy and weird for your users as resources load. | ||
For general guidance, please check out the `test` folder to see how you might use placeholders with yall.js. If you don't want to bother with placeholders, you _can_ omit them. yall.js will still work, though the experience may be weird and jumpy and weird for your users as resources load. | ||
@@ -245,3 +285,3 @@ Also, do _not_ lazy load resources that are likely to near the top of the page ("above the fold", as it were). Doing so is a performance anti-pattern, because those resources will not begin loading until yall.js has been loaded, which may take much longer than if those resources were loaded normally. | ||
If you have an idea, file an issue and let's talk about it. Unsolicited pull requests for new features will generally be rejected unless those requests contain bug fixes. Please see [`CONTRIBUTING.md`](https://github.com/malchata/yall.js/blob/master/CONTRIBUTING.md) for more details. | ||
If you have an idea, file an issue and let's talk about it. Unsolicited pull requests for new features will generally be rejected unless those requests contain bug fixes. Please see [`CONTRIBUTING.md`](https://github.com/malchata/yall.js/blob/asfaloth/CONTRIBUTING.md) for more details. If your issue is related to version 3, please indicate as such in your issue. | ||
@@ -248,0 +288,0 @@ ## Special thanks |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Install scripts
Supply chain riskInstall scripts are run when the package is installed. The majority of malware in npm is hidden in install scripts.
Found 1 instance in 1 package
279
28324
208
1