lazy load -vanilla
by ian_smithz
HTML
<header>
<div class=hero_bg></div>
<h1>Performant lazy-load method using the Intersection Observer API</h1>
</header>
<section>
<h2 class=centered-1>No visual jank, runs at the full 60fps</h2>
<!-- <img data-LL-src="https://source.unsplash.com/pFqrYbhIAXs/?w=1920x1080"> -->
<img data-LL-src="https://websemantics.uk/unsplash/needle-on-the-record.jpg">
<noscript>
<img src="https://websemantics.uk/unsplash/needle-on-the-record.jpg">
</noscript>
<a href="https://unsplash.com/@lukechesser" target=_blank rel="noopener noreferrer">
<svg><use xlink:href="#icon_camera"></use></svg>
Luke Chesser
</a>
</section>
<section>
<h2 class=centered-2>Ultra lightweight vanilla JavaScript</h2>
<!-- <img data-LL-src="https://source.unsplash.com/bq-1seg1cPI/?w=1920x1080"> -->
<img data-LL-src="https://websemantics.uk/unsplash/busy-bike.jpg">
<noscript>
<img src="https://websemantics.uk/unsplash/busy-bike.jpg">
</noscript>
<a href="https://unsplash.com/@clemono2" target=_blank rel="noopener noreferrer">
<svg><use xlink:href="#icon_camera"></use></svg>
Clem Onojeghuo
</a>
</section>
<section>
<h2 class=centered-3>Images loaded when JavaScript is unavailable</h2>
<!-- <img data-LL-src="https://source.unsplash.com/X2gM-SIufpU/?w=1920x2390"> -->
<img data-LL-src="https://websemantics.uk/unsplash/sunday-delight.jpg">
<noscript>
<img src="https://websemantics.uk/unsplash/sunday-delight.jpg">
</noscript>
<a href="https://unsplash.com/@foodess" target=_blank rel="noopener noreferrer">
<svg><use xlink:href="#icon_camera"></use></svg>
Jennifer Pallian
</a>
</section>
<section>
<h2 class=centered-4>Images loaded when IntersectionObserver is unsupported.</h2>
<!-- <img data-LL-src="https://source.unsplash.com/AYSorKGDm10/?w=1920x1080"> -->
<img data-LL-src="https://websemantics.uk/unsplash/red-door.jpg">
<noscript>
<img...
CSS
@media (min-width: 20em) {
:root {
font-size: calc(1rem + ((1vw - 0.2em) * 1));
}
}
@media (min-width: 120em) {
:root {
font-size: 2em;
}
}
body {
font-family: sans-serif;
line-height: 1.5;
margin: 0;
text-align: center;
}
h1 {
color: #fff;
font-size: 1.5em;
font-weight: 100;
margin: 1rem;
padding: 1rem .5rem;
position: absolute; /* Collapses box */
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-shadow: 0 0 2px #000, 0 1px 1px #000;
background-color: rgba(0,0,0, .5);
/* backdrop-filter: grayscale(1) contrast(3) blur(5px); */
}
h2 {
margin: 2em 0 1em;
font-size: 1.25em;
font-weight: 100;
}
[class^="centered"] {
color: #fff;
background-color: rgba(0,0,0, .5);
position: absolute; /* Collapses box */
top: 50px;
left: 50%;
transform: translate(-50%, 0);
text-shadow: 0 0 2px #000, 0 1px 1px #000;
letter-spacing: 0.05em;
/* white-space: nowrap; */
z-index: 1;
margin:0;
padding: .25em .5em;
}
header,
footer,
section {
display: block;
position: relative;
min-height: calc(100vw / 16 * 10);
}
footer {
min-height: 0;
max-width: 36em;
margin: 0 auto;
padding: 0 1rem;
}
.myStuff, .myStuff .details_lnk {
background-color: #f7f7f7;
}
.hero_bg {
position: absolute;
width: 100%;
top: 0;
right: 0;
bottom: 0;
left;
background-image: url(https://websemantics.uk/unsplash/duvet-days.jpg);
background-size:cover;
/* filter: blur(1px); */
}
img {
display:block;
max-width: 100%;
margin:0 auto;
}
a {
position: absolute;
right: 0;
bottom: 0;
background-color: rgba(0,0,0,.5);
color: #fff;
text-decoration: none;
padding: .25em 1em;
}
.details_lnk {
position: relative;
padding: 0;
text-decoration: underline;
background-color: #fff;
color: #000;
}
svg {
width: .9em;
height: .9em;
vertical-align: text-top;
fill: #ccc;
margin-right:.35em;
}
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
...
JavaScript
// Dean Hume: https://deanhume.github.io/lazy-observer-load/
// TODO: background-images
var lazy_loader = (() => {
"use strict";
const dataAttr = "data-LL-src";
const imageLoaded = "-js-lazyImageLoaded";
const imageFadeClass = "-js-fadeIn";
// Get all of the images that are marked up to lazy load
const images = document.querySelectorAll("[" + dataAttr + "]");
const config = {
// If the image gets within 50px in the Y axis, start the download.
//rootMargin: '50px 0px',
rootMargin: '-50px 0px', // 50px into viewport before loading so you can see it kick in
threshold: 0.01
};
let imageCount = images.length;
let observer;
// If we don't have support for intersection observer, loads the images immediately
if (!('IntersectionObserver' in window)) {
console.log("No IntersectionObserver support");
Array.from(images).forEach(image => preloadImage(image));
} else {
// It is supported, load the images
console.log("IntersectionObserver supported");
observer = new IntersectionObserver(onIntersection, config);
images.forEach(image => {
if (image.classList.contains(imageLoaded)) {
return;
}
observer.observe(image);
});
}
// Fetch image for given URL
function fetchImage(url) {
return new Promise((resolve, reject) => {
const image = new Image();
image.src = url;
image.onload = resolve;
image.onerror = reject;
});
}
// Preloads the image
function preloadImage(image) {
const src = image.getAttribute(dataAttr);
if (!src) {
return;
}
return fetchImage(src).then(() => {
applyImage(image, src);
});
}
// Load all of the images immediately
function loadImagesImmediately(images) {
Array.from(images).forEach(image => preloadImage(image));
}
// Disconnect the observer
function disconnect() {
if (!observer) {
return;
}
observer.disconnect();
}
// On intersection
...