Comparing version 3.2.0 to 4.0.0
158
dist/yall.js
@@ -1,156 +0,2 @@ | ||
'use strict'; | ||
function yall (options) { | ||
options = options || {}; | ||
// Options | ||
const lazyClass = options.lazyClass || "lazy"; | ||
const lazyBackgroundClass = options.lazyBackgroundClass || "lazy-bg"; | ||
const idleLoadTimeout = "idleLoadTimeout" in options ? options.idleLoadTimeout : 200; | ||
const observeChanges = options.observeChanges || false; | ||
const events = options.events || {}; | ||
const noPolyfill = options.noPolyfill || false; | ||
// Shorthands (saves more than a few bytes!) | ||
const win = window; | ||
const ric = "requestIdleCallback"; | ||
const io = "IntersectionObserver"; | ||
const ioSupport = io in win && `${io}Entry` in win; | ||
// App stuff | ||
const crawler = /baidu|(?:google|bing|yandex|duckduck)bot/i.test(navigator.userAgent); | ||
const dataAttrs = ["srcset", "src", "poster"]; | ||
const arr = []; | ||
const queryDOM = (selector, root) => arr.slice.call((root || document).querySelectorAll(selector || `img.${lazyClass},video.${lazyClass},iframe.${lazyClass},.${lazyBackgroundClass}`)); | ||
// This function handles lazy loading of elements. | ||
const yallLoad = element => { | ||
const parentNode = element.parentNode; | ||
if (parentNode.nodeName == "PICTURE") { | ||
yallApplyFn(queryDOM("source", parentNode), yallFlipDataAttrs); | ||
} | ||
if (element.nodeName == "VIDEO") { | ||
yallApplyFn(queryDOM("source", element), yallFlipDataAttrs); | ||
} | ||
yallFlipDataAttrs(element); | ||
const classList = element.classList; | ||
// Lazy load CSS background images | ||
if (classList.contains(lazyBackgroundClass)) { | ||
classList.remove(lazyBackgroundClass); | ||
classList.add(options.lazyBackgroundLoaded || "lazy-bg-loaded"); | ||
} | ||
}; | ||
const yallBindEvents = element => { | ||
for (let eventIndex in events) { | ||
element.addEventListener(eventIndex, events[eventIndex].listener || events[eventIndex], events[eventIndex].options || undefined); | ||
} | ||
}; | ||
// Added because there was a number of patterns like this peppered throughout | ||
// the code. This flips necessary data- attrs on an element and prompts video | ||
// elements to begin playback automatically if they have autoplay specified. | ||
const yallFlipDataAttrs = element => { | ||
for (let dataAttrIndex in dataAttrs) { | ||
if (dataAttrs[dataAttrIndex] in element.dataset) { | ||
element.setAttribute(dataAttrs[dataAttrIndex], element.dataset[dataAttrs[dataAttrIndex]]); | ||
const parentNode = element.parentNode; | ||
if (element.nodeName === "SOURCE" && parentNode.autoplay) { | ||
parentNode.load(); | ||
// For some reason, IE11 needs to have this method invoked in order | ||
// for autoplay to start. So we do a yucky user agent check. | ||
if (/Trident/.test(navigator.userAgent)) { | ||
parentNode.play(); | ||
} | ||
parentNode.classList.remove(lazyClass); | ||
} | ||
element.classList.remove(lazyClass); | ||
} | ||
} | ||
}; | ||
// Noticed lots of loops where a function simply gets executed on every | ||
// member of an array. This abstraction eliminates that repetitive code. | ||
const yallApplyFn = (items, fn) => { | ||
for (let itemIndex = 0; itemIndex < items.length; itemIndex++) { | ||
win[io] && fn instanceof win[io] ? fn.observe(items[itemIndex]) : fn(items[itemIndex]); | ||
} | ||
}; | ||
const yallCreateMutationObserver = entry => { | ||
new MutationObserver(() => { | ||
yallApplyFn(queryDOM(), newElement => { | ||
if (lazyElements.indexOf(newElement) < 0) { | ||
lazyElements.push(newElement); | ||
yallBindEvents(newElement); | ||
if (ioSupport && !crawler) { | ||
intersectionListener.observe(newElement); | ||
} else if (noPolyfill || crawler) { | ||
yallApplyFn(lazyElements, yallLoad); | ||
} | ||
} | ||
}); | ||
}).observe(entry, options.mutationObserverOptions || { | ||
childList: true, | ||
subtree: true | ||
}); | ||
}; | ||
let lazyElements = queryDOM(); | ||
yallApplyFn(lazyElements, yallBindEvents); | ||
// First we check if IntersectionObserver is supported. If not, we check to | ||
// see if the `noPolyfill` option is set. If so, we load everything. If the | ||
// current user agent is a known crawler, again, we load everything. | ||
if (ioSupport && !crawler) { | ||
var intersectionListener = new win[io](entries => { | ||
yallApplyFn(entries, entry => { | ||
if (entry.isIntersecting || entry.intersectionRatio) { | ||
const element = entry.target; | ||
if (ric in win && idleLoadTimeout) { | ||
win[ric](() => { | ||
yallLoad(element); | ||
}, { | ||
timeout: idleLoadTimeout | ||
}); | ||
} else { | ||
yallLoad(element); | ||
} | ||
intersectionListener.unobserve(element); | ||
lazyElements = lazyElements.filter(lazyElement => lazyElement != element); | ||
// If all the elements that were detected at load time are all loaded | ||
// and we're not observing for changes, we're all done here. | ||
if (!lazyElements.length && !observeChanges) { | ||
intersectionListener.disconnect(); | ||
} | ||
} | ||
}); | ||
}, { | ||
rootMargin: `${"threshold" in options ? options.threshold : 200}px 0%` | ||
}); | ||
yallApplyFn(lazyElements, intersectionListener); | ||
if (observeChanges) { | ||
yallApplyFn(queryDOM(options.observeRootSelector || "body"), yallCreateMutationObserver); | ||
} | ||
} else if (noPolyfill || crawler) { | ||
yallApplyFn(lazyElements, yallLoad); | ||
} | ||
} | ||
module.exports = yall; | ||
function e(e,o){for(const t in o){const n=o[t];e.addEventListener(t,n.listener||n,n.options||void 0)}}const o="IntersectionObserver"in window&&"IntersectionObserverEntry"in window&&"intersectionRatio"in window.IntersectionObserverEntry.prototype,t=/baidu|(?:google|bing|yandex|duckduck)bot/i.test(navigator.userAgent),n=["src","poster"];function r(e,o){for(const t of n)t in e.dataset&&(e.setAttribute(t,e.dataset[t]),e.classList.contains(o)&&e.classList.remove(o))}function s(e,o,t,n){if("VIDEO"==e.nodeName){const t=Array.from(e.querySelectorAll("source"));for(const e of t)r(e,o);e.load()}r(e,o);const s=e.classList;s.contains(t)&&(s.remove(t),s.add(n))}function i(n){const r=n?.lazyClass||"lazy",i=n?.lazyBackgroundClass||"lazy-bg",c=n?.lazyBackgroundLoaded||"lazy-bg-loaded",a=n?.threshold||200,l=n?.events||{},d=n?.observeChanges||!1,f=n?.observeRootSelector||"body",u=n?.mutationObserverOptions||{childList:!0,subtree:!0},b=`video.${r},.${i}`;let v=Array.from(document.querySelectorAll(b));for(const o of v)e(o,l);if(!0===o&&!1===t){var y=new IntersectionObserver(e=>{for(const o of e)if(o.isIntersecting||o.intersectionRatio){const{target:e}=o;s(e,r,i,c),y.unobserve(e),v=v.filter(o=>o!=e),0===v.length&&!1===d&&y.disconnect()}},{rootMargin:`${a}px 0%`});for(const e of v)y.observe(e);d&&new MutationObserver(()=>{const n=document.querySelectorAll(b);for(const r of n)!1===v.includes(r)&&(v.push(r),e(r,l),!0===o&&!1===t&&y.observe(r))}).observe(document.querySelector(f),u)}else if(t)for(const e of v)s(e,r,i,c)}export{i as yall}; | ||
//# sourceMappingURL=yall.js.map |
{ | ||
"name": "yall-js", | ||
"version": "3.2.0", | ||
"version": "4.0.0", | ||
"description": "Yet Another Lazy Loader", | ||
"main": "./dist/yall.js", | ||
"module": "./src/yall.mjs", | ||
"jsnext:main": "./src/yall.mjs", | ||
"type": "module", | ||
"source": "./src/index.js", | ||
"exports": "./dist/yall.mjs", | ||
"main": "./dist/yall.cjs", | ||
"module": "./dist/yall.js", | ||
"unpkg": "./dist/yall.umd.js", | ||
"scripts": { | ||
"clean": "npx rimraf ./dist", | ||
"lint": "npx eslint ./src/yall.mjs", | ||
"test": "npm run lint && npm run build && cp -fv ./dist/yall.min.js ./test/js && npm run server", | ||
"server": "node http.js", | ||
"build": "npm run clean && npx rollup -c", | ||
"clean": "rm -rf ./dist", | ||
"build": "npm run clean && npx microbundle && npm run copy", | ||
"copy": "cp -v dist/yall.js ./test/js/yall.js && cp -v dist/yall.js.map ./test/js/yall.js.map", | ||
"dev": "npm run clean && npx microbundle watch", | ||
"lint": "npx eslint ./src/**.js ./src/**/*.js", | ||
"test": "npm run lint && npm run build && node ./http.js", | ||
"postinstall": "node -e \"console.log('\\u001b[31m\\u001b[1mThanks for using yall.js! Excluding bugfixes, development of yall.js is finished. You should also consider replacing yall.js with native lazy loading if you can. Learn more at \\u001b[34mhttps://web.dev/native-lazy-loading/\\u001b[0m')\"" | ||
@@ -42,14 +46,12 @@ }, | ||
"devDependencies": { | ||
"@babel/cli": "^7.8.4", | ||
"@babel/core": "^7.9.0", | ||
"@babel/preset-env": "^7.9.0", | ||
"eslint": "^6.8.0", | ||
"express": "^4.17.1", | ||
"rimraf": "^3.0.2", | ||
"rollup": "^2.3.2", | ||
"rollup-plugin-babel": "^4.4.0", | ||
"rollup-plugin-copy": "^3.3.0", | ||
"rollup-plugin-terser": "^5.3.0" | ||
"@babel/cli": "^7.17.10", | ||
"@babel/core": "^7.18.2", | ||
"@babel/preset-env": "^7.18.2", | ||
"eslint": "^8.16.0", | ||
"express": "^4.18.1", | ||
"microbundle": "^0.15.0" | ||
}, | ||
"dependencies": {} | ||
"files": [ | ||
"dist" | ||
] | ||
} |
192
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/main/dist/yall.min.js?label=Uncompressed" alt="Uncompressed size."> <img src="https://img.badgesize.io/malchata/yall.js/main/dist/yall.min.js?compression=gzip&label=gzip" alt="gzip size."> <img src="https://img.badgesize.io/malchata/yall.js/main/dist/yall.min.js?compression=brotli&label=brotli" alt="Brotli size."> | ||
</p> | ||
@@ -14,9 +14,7 @@ <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/main/dist/yall.min.mjs?label=Uncompressed" alt="Uncompressed size."> <img src="https://img.badgesize.io/malchata/yall.js/main/dist/yall.min.mjs?compression=gzip&label=gzip" alt="gzip size."> <img src="https://img.badgesize.io/malchata/yall.js/main/dist/yall.min.mjs?compression=brotli&label=brotli" alt="Brotli size."> | ||
</p> | ||
**Note: Barring any bugfixes, development of yall.js is done. Additionally, consider using [native lazy loading](https://web.dev/native-lazy-loading/) instead of any JavaScript-based solution unless you need to lazy load resources other than images and iframes.** | ||
yall.js is a SEO-friendly lazy loader for `<video>` elements as well as CSS background images. It works in all modern browsers. It uses [Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API) where available. yall.js 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 elements that have been appended to the DOM after initial load, which may be desirable for single page applications. | ||
yall.js is a featured-packed SEO-friendly lazy loader for `<img>`, `<picture>`, `<video>` and `<iframe>` elements, as well as CSS background images. It works in all modern browsers, including IE 11. 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 elements that have been appended to the DOM after initial load, 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), and reduces jank by using [`requestAnimationFrame`](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame). | ||
To use yall, 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 it with npm: | ||
@@ -33,3 +31,7 @@ | ||
```javascript | ||
document.addEventListener("DOMContentLoaded", yall); | ||
// Import y'all | ||
import { yall } from "yall-js"; | ||
// Invoke! | ||
yall(); | ||
``` | ||
@@ -40,51 +42,13 @@ | ||
```javascript | ||
document.addEventListener("DOMContentLoaded", function() { | ||
yall({ | ||
observeChanges: true | ||
}); | ||
// Import y'all | ||
import { yall } from "yall-js"; | ||
// Invoke! | ||
yall({ | ||
observeChanges: true | ||
}); | ||
``` | ||
Unlike version 2 which provided a fallback for browsers without `IntersectionObserver` support, version 3 doesn't. If you need to support browsers that don't support `IntersectionObserver`, 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>` | ||
Here's a typical `<img>` element use case: | ||
```html | ||
<!-- An src-only <img> element example --> | ||
<img class="lazy" src="placeholder.jpg" data-src="image-to-lazy-load.jpg" alt="Alternative text to describe image."> | ||
``` | ||
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 can be overridden in the options). Let's look at an example using both `src` and `srcset`: | ||
```html | ||
<!-- A somewhat more complex src + srcset example --> | ||
<img class="lazy" src="placeholder.jpg" data-srcset="image-to-lazy-load-2x.jpg 2x, image-to-lazy-load-1x.jpg 1x" data-src="image-to-lazy-load-1x.jpg" alt="Alternative text to describe image."> | ||
``` | ||
Mostly the same as before, just another `data-` attributes for `srcset` is all. | ||
**Note:** If you're using `media` and `sizes` attributes, _do not_ prefix them with `data-`. Leave them as-is. | ||
### `<picture>` | ||
Since `<picture>` is a thing, yall.js supports that, too: | ||
```html | ||
<!-- A more complex <picture> + <img> + src/srcset example --> | ||
<picture> | ||
<source data-srcset="image-to-lazy-load-2x.webp 2x, image-to-lazy-load-1x.webp 1x" type="image/webp"> | ||
<img class="lazy" src="placeholder.jpg" data-srcset="image-to-lazy-load-2x.jpg 2x, image-to-lazy-load-1x.jpg 1x" data-src="image-to-lazy-load-1x.jpg" alt="Alternative text to describe image."> | ||
</picture> | ||
``` | ||
Not too much different than the `<img>` use cases, except you're pointing to the images you want to load in the `<source>` element(s) as well as the `<img>` element. | ||
### `<video>` | ||
@@ -126,10 +90,2 @@ | ||
### `<iframe>` | ||
You can also lazy load `<iframe>`s: | ||
```html | ||
<iframe class="lazy" data-src="some-other-document.html"></iframe> | ||
``` | ||
### CSS images | ||
@@ -167,22 +123,4 @@ | ||
```html | ||
<!-- A <noscript> example using <img> with src and srcset. --> | ||
<img class="lazy" data-srcset="/img/image-to-lazy-load-2x.jpg 2x, /img/image-to-lazy-load-1x.jpg 1x" data-src="/img/image-to-lazy-load-1x.jpg" src="/img/placeholder.jpg" alt="Alternative text to describe image."> | ||
<noscript> | ||
<img srcset="/img/image-to-lazy-load-2x.jpg 2x, /img/image-to-lazy-load-1x.jpg 1x" src="/img/image-to-lazy-load-1x.jpg" alt="Alternative text to describe image."> | ||
</noscript> | ||
<!-- And a <picture> example. --> | ||
<picture> | ||
<source data-srcset="/img/image-to-lazy-load-2x.webp 2x, /img/image-to-lazy-load-1x.webp 1x" type="image/webp"> | ||
<img class="lazy" data-srcset="/img/image-to-lazy-load-2x.jpg 2x, /img/image-to-lazy-load-1x.jpg 1x" data-src="/img/image-to-lazy-load-1x.jpg" src="/img/placeholder.jpg" alt="Alternative text to describe image."> | ||
</picture> | ||
<noscript> | ||
<picture> | ||
<source srcset="/img/image-to-lazy-load-2x.webp 2x, /img/image-to-lazy-load-1x.webp 1x" type="image/webp"> | ||
<img srcset="/img/image-to-lazy-load-2x.jpg 2x, /img/image-to-lazy-load-1x.jpg 1x" src="/img/image-to-lazy-load-1x.jpg" alt="Alternative text to describe image."> | ||
</picture> | ||
</noscript> | ||
<!-- Here's a <video> example, too. --> | ||
<video class="lazy" autoplay loop muted playsinline> | ||
<!-- A `<video>` example: --> | ||
<video class="lazy" preload="none" autoplay loop muted playsinline> | ||
<source data-src="video.webm" type="video/webm"> | ||
@@ -192,3 +130,3 @@ <source data-src="video.mp4" type="video/mp4"> | ||
<noscript> | ||
<video autoplay loop muted playsinline> | ||
<video preload="none" autoplay loop muted playsinline> | ||
<source src="video.webm" type="video/webm"> | ||
@@ -199,6 +137,12 @@ <source src="video.mp4" type="video/mp4"> | ||
<!-- Here's an <iframe> example for good measure. --> | ||
<iframe class="lazy" data-src="lazy.html"></iframe> | ||
<!-- A `<video>` example: --> | ||
<video class="lazy" preload="none" autoplay loop muted playsinline data-poster="img/video-poster.jpg"> | ||
<source src="video.webm" type="video/webm"> | ||
<source src="video.mp4" type="video/mp4"> | ||
</video> | ||
<noscript> | ||
<iframe src="lazy.html"></iframe> | ||
<video preload="none" autoplay loop muted playsinline poster="img/video-poster.jpg"> | ||
<source src="video.webm" type="video/webm"> | ||
<source src="video.mp4" type="video/mp4"> | ||
</video> | ||
</noscript> | ||
@@ -250,7 +194,2 @@ ``` | ||
### `idleLoadTimeout` | ||
**default:** `200`<br> | ||
In environments where `requestIdleCallback` is available, this option sets a deadline in milliseconds to kick off lazy loading for elements. If this is set to `0`, `requestIdleCallback` is never called, and lazy loading for elements will begin immediately once they're in the viewport. | ||
### `threshold` | ||
@@ -266,31 +205,29 @@ | ||
```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"); | ||
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.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 == "VIDEO") { | ||
event.target.classList.add("yall-error"); | ||
} | ||
}, | ||
// 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 | ||
} | ||
// 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 | ||
} | ||
} | ||
}); | ||
} | ||
}); | ||
@@ -318,33 +255,18 @@ ``` | ||
### `noPolyfill` | ||
**default:** false | ||
If `noPolyfill` is set to `true` yall.js will assume you are not polyfilling `IntersectionObserver`, and will subsequently load all resources when it detects no support for `IntersectionObserver`. This option will save you ~2.4kB for the `intersection-observer` polyfill, but be advised that invoking this option means that you could potentially load both placeholders _and_ the final image sources. Additionally, you'll be dependent on JavaScript to trigger immediate loading of all images in the document for browsers that don't support `IntersectionObserver`. For these reasons, it's only advised to enable this option if the vast majority of your users are on browsers that support `IntersectionObserver`. | ||
## Words of advice | ||
This script aims to provide a reasonable level of compatibility down to IE 11, but as stated previously, you will need to polyfill `IntersectionObserver` for yall.js to work in that browser. If you don't polyfill `IntersectionObserver`, non-supporting browsers won't throw an error, they'll fail silently. However, features that are natively available in at least IE 11 (such as `MutationObserver` and `requestAnimationFrame` will not be checked for, and _will_ throw errors if they are not available. For example, because `requestIdleCallback` is not available in IE 11, it _will_ be checked for. If it doesn't exist, it will simply not be used. Polyfill it if you need it. | ||
Unlike previous versions of yall-js, compatibility back to IE 11 is no longer a goal. If you need compatibility with older browsers, install the previous release of yall.js like so: | ||
Also, it is not this script's job to minimize layout shifting for you. [Use appropriate `width` and `height` attributes](https://www.smashingmagazine.com/2020/03/setting-height-width-images-important-again/), styles, and lightweight placeholders for your images. | ||
```shell | ||
npm i yall@3.2.0 | ||
``` | ||
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. Or you can opt _not_ to use a `poster` image. Your website, your call. | ||
Also, it is not this script's job to minimize [layout shifts](https://web.dev/cls/) for you. [Use appropriate `width` and `height` attributes](https://www.smashingmagazine.com/2020/03/setting-height-width-images-important-again/), styles, and lightweight placeholders for your images. | ||
Also, do _not_ lazy load resources that are likely to near the top of the page—or "above the fold", as it were. Doing so is an anti-pattern in that those resources will not begin loading until yall.js has been loaded, which may take much longer than if those resources were loaded normally. | ||
For `<video>` elements, 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. Or you can opt _not_ to use a `poster` image. Your website, your call. | ||
## Integration | ||
Also, do _not_ lazy load resources that are likely to near the top of the page—or "above the fold", as it were. Doing so is an anti-pattern in that those resources will not begin loading until yall.js has been loaded, which may take much longer than if those resources were loaded normally. Such a pattern will negatively affect your page's [LCP](https://web.dev/lcp/). | ||
Here are some external libraries or extensions built on top `malchata/yall.js`. | ||
- [`adhocore/twig-yall`](https://github.com/adhocore/twig-yall) - A twig template engine extension that makes integration of `malchata/yall.js` a breeze. | ||
## Contributing | ||
Please read the [contribution guidelines](https://github.com/malchata/yall.js/blob/master/CONTRIBUTING.md). If you think I'm some kind of grumpy crank after reading that, please remember that this is a hobby project you can use for free. Here's a couple other options for you if yall.js doesn't do what you need it to: | ||
- [Lozad.js](https://github.com/ApoorvSaxena/lozad.js) | ||
- [lazysizes](https://github.com/aFarkas/lazysizes) | ||
## Special thanks | ||
Thank you to [BrowserStack](https://www.browserstack.com/) for graciously providing free cross-platform browser testing services! | ||
[![BrowserStack](https://res.cloudinary.com/drp9iwjqz/image/upload/f_auto,q_auto/v1527175969/browserstack_txnmf8.png)](https://www.browserstack.com/) | ||
Please read the [contribution guidelines](https://github.com/malchata/yall.js/blob/master/CONTRIBUTING.md). If you think I'm some kind of grumpy crank after reading that, please remember that this is a hobby project you can use for free. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
53048
6
10
Yes
30
262
1