<!DOCTYPE html>
<html lang="en" >
<head>
<meta charset="UTF-8">
<title>Performant lazy-load technique for 2018</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<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 src="https://websemantics.uk/unsplash/red-door.jpg">
</noscript>
<a href="https://unsplash.com/@shttefan" target=_blank rel="noopener noreferrer">
<svg><use xlink:href="#icon_camera"></use></svg>
SHTTEFAN
</a>
</section>
<section>
<h2 class=centered>Neat huh?</h2>
<!-- <img data-LL-src="https://source.unsplash.com/PC_lbSSxCZE/?w=1920x1080"> -->
<img data-LL-src="https://websemantics.uk/unsplash/excalibur-sparkles.jpg">
<noscript>
<img src="https://websemantics.uk/unsplash/excalibur-sparkles.jpg">
</noscript>
<a href="https://unsplash.com/@krisroller" target=_blank rel="noopener noreferrer">
<svg><use xlink:href="#icon_camera"></use></svg>
Kristopher Roller
</a>
</section>
<footer>
<h2>Details</h2>
<p>Method utilises the new Intersection Observer API. Which has zero jank and runs at the full 60fps using ultra lightweight vanilla JavaScript.</p>
<p>Under fallback conditions (method unsupported, no JavaScript available) images are loaded as normal.</p>
<h2>Credits</h2>
<p>Based heavily on Dean Humes article: <a class=details_lnk title="[new window]" target=_blank href="http://www.deanhume.com/Home/BlogPost/lazy-loading-images-using-intersection-observer/10163">Lazy-loading images using Intersection Observer</a></p>
<p>Images from <a class=details_lnk target=_blank title="[new window]" href="https://unsplash.com/">Unsplash</a></p>
<h2>More info</h2>
<p>MDN: <a class=details_lnk target=_blank title="[new window]" href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API">Intersection Observer API</a></p>
<p>Google Developers: <a class=details_lnk target=_blank title="[new window]" href="https://developers.google.com/web/updates/2016/04/intersectionobserver">IntersectionObserver’s coming into view</a></p>
<p>Can I Use: <a class=details_lnk target=_blank title="[new window]" href="http://caniuse.com/#feat=intersectionobserver">browser support</a></p>
<p class=myStuff><a class=details_lnk target=_blank title="[new window]" href="https://codepen.io/2kool2/pens/popular/?grid_type=list#">Pens by Mike Foskett</a> — <a class=details_lnk target=_blank title="[new window]" href="https://websemantics.uk/">webSemantics</a></p>
</footer>
<svg class=visually-hidden>
<defs>
<symbol id="icon_camera" viewBox="0 0 32 32">
<path d="M20.8 18.1c0 2.7-2.2 4.8-4.8 4.8s-4.8-2.1-4.8-4.8c0-2.7 2.2-4.8 4.8-4.8 2.7.1 4.8 2.2 4.8 4.8zm11.2-7.4v14.9c0 2.3-1.9 4.3-4.3 4.3h-23.4c-2.4 0-4.3-1.9-4.3-4.3v-15c0-2.3 1.9-4.3 4.3-4.3h3.7l.8-2.3c.4-1.1 1.7-2 2.9-2h8.6c1.2 0 2.5.9 2.9 2l.8 2.4h3.7c2.4 0 4.3 1.9 4.3 4.3zm-8.6 7.5c0-4.1-3.3-7.5-7.5-7.5-4.1 0-7.5 3.4-7.5 7.5s3.3 7.5 7.5 7.5c4.2-.1 7.5-3.4 7.5-7.5z"></path>
</symbol>
</defs>
</svg>
<script src="js/index.js"></script>
</body>
</html>
/*Downloaded from https://www.codeseek.co/2kool2/performant-lazy-load-technique-for-2017-and-onward-XavqyY */
@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;
overflow: hidden;
clip: rect(1px, 1px, 1px, 1px);
}
.-js-fadeIn {
animation-name: fadeIn;
animation-duration: 2s;
animation-timing-function: cubic-bezier(0, 0, 0.4, 1);
animation-fill-mode: forwards;
}
@keyframes fadeIn {
from {opacity: 0;}
to {opacity: 1;}
}
/*Downloaded from https://www.codeseek.co/2kool2/performant-lazy-load-technique-for-2017-and-onward-XavqyY */
// 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
function onIntersection(entries) {
// Disconnect if we've already loaded all of the images
if (imageCount === 0) {
observer.disconnect();
}
// Loop through the entries
entries.forEach(entry => {
// Are we in viewport?
if (entry.intersectionRatio > 0) {
imageCount--;
// Stop watching and load the image
observer.unobserve(entry.target);
preloadImage(entry.target);
}
});
}
// Apply the image
function applyImage(img, src) {
// Prevent this from being lazy loaded a second time.
img.classList.add(imageLoaded);
img.src = src;
img.classList.add(imageFadeClass);
}
})();