<h1>How to lazy load images in a performant way</h1>
<p>
There is also an example image how to use it with an background image.
<br>
Take a look at the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Browser_compatibility">Browser support table.</a>
and use <a href="https://www.npmjs.com/package/intersection-observer#browser-support">Polyfill</a> for better support. This one here does work properly in Safari, thanks to the <a href="https://polyfill.io/v2/polyfill.min.js?features=IntersectionObserver">Polyfill Script</a> I added to the source. You will see that the HTML is bloated up quite a bit, this is because I added some noscript fallbacks for no-js browsing people.</p>
<noscript>
<!--
Hide the lazy load images if we do not have JS
-->
<style>
.image__list-lazy-image {
display: none;
}
</style>
</noscript>
<div class="image-container">
<ul class="image__list">
<li class="image__list-item">
<noscript>
<img class="image__list-image" src="http://res.cloudinary.com/dsteinel/image/upload/c_scale,w_450/v1523796300/pexels-photo-559768.jpg" alt="blue lake with surrounding mountains">
</noscript>
<img class="image__list-image image__list-lazy-image" data-src="http://res.cloudinary.com/dsteinel/image/upload/c_scale,w_450/v1523796300/pexels-photo-559768.jpg" alt="blue lake with surrounding mountains">
</li>
<li class="image__list-item">
<noscript>
<img class="image__list-image" src="http://res.cloudinary.com/dsteinel/image/upload/c_scale,w_450/v1523796305/pexels-photo-104336.jpg" alt="street in the middle of a forrest">
</noscript>
<img class="image__list-image image__list-lazy-image" data-src="http://res.cloudinary.com/dsteinel/image/upload/c_scale,w_450/v1523796305/pexels-photo-104336.jpg" alt="street in the middle of a forrest">
</li>
<li class="image__list-item">
<noscript>
<img class="image__list-image" src="http://res.cloudinary.com/dsteinel/image/upload/c_scale,w_450/v1523796376/les-anderson-175603.jpg" alt="two elder people opening togehter a christmas present">
</noscript>
<img class="image__list-image image__list-lazy-image" data-src="http://res.cloudinary.com/dsteinel/image/upload/c_scale,w_450/v1523796376/les-anderson-175603.jpg" alt="two elder people opening togehter a christmas present">
</li>
<li class="image__list-item">
<noscript>
<img class="image__list-image" src="http://res.cloudinary.com/dsteinel/image/upload/c_scale,w_450/v1523796381/pexels-photo-737108.jpg" alt="black and white clothes hanging on a clothes rail">
</noscript>
<img class="image__list-image image__list-lazy-image" data-src="http://res.cloudinary.com/dsteinel/image/upload/c_scale,w_450/v1523796381/pexels-photo-737108.jpg" alt="black and white clothes hanging on a clothes rail">
</li>
<li class="image__list-item">
<noscript>
<img class="image__list-image" src="http://res.cloudinary.com/dsteinel/image/upload/c_scale,w_450/v1523796475/tobias-van-schneider-122288.jpg" alt="book collection photographed from top">
</noscript>
<img class="image__list-image image__list-lazy-image" data-src="http://res.cloudinary.com/dsteinel/image/upload/c_scale,w_450/v1523796475/tobias-van-schneider-122288.jpg" alt="book collection photographed from top">
</li>
<li class="image__list-item">
<noscript>
<img class="image__list-image" src="http://res.cloudinary.com/dsteinel/image/upload/c_scale,w_450/v1523796300/pexels-photo-559768.jpg" alt="two elder people opening togehter a christmas present">
</noscript>
<img class="image__list-image image__list-lazy-image" data-src="http://res.cloudinary.com/dsteinel/image/upload/c_scale,w_450/v1523796300/pexels-photo-559768.jpg" alt="brown meadow with a tower in the very back">
</li>
<li class="image__list-item">
<noscript>
<div class="image__list-image--bg" style="background-image:url('http://res.cloudinary.com/dsteinel/image/upload/c_scale,w_450/v1523796300/pexels-photo-559768.jpg');" alt="two elder people opening togehter a christmas present"></div>
</noscript>
<div class="image__list-image--bg image__list-lazy-image" data-lazybackground="http://res.cloudinary.com/dsteinel/image/upload/c_scale,w_450/v1523796300/pexels-photo-559768.jpg">
</div>
</li>
</ul>
</div>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
max-width: 450px;
margin: 0 auto;
padding: 2em 0;
font-size: 18px;
line-height: 1.5;
}
h1 {
display: block;
margin: 0 auto 2rem;
line-height: 1.1;
}
ul {
padding: 0;
margin: 0;
list-style-type: none;
}
a {
color: #A55; /* HAHA! */
}
p {
margin-bottom: 3rem;
}
.image__list-image {
opacity: 1;
min-height: 100%;
}
.image__list-lazy-image {
opacity: 0;
}
.image__list-item {
width: 100%;
height: 250px;
overflow: hidden;
margin-bottom: 40px;
}
.image__list-image--bg {
background-size: cover;
background-repeat: no-repeat;
height: 100%;
}
const options = {
rootMargin: '0px',
threshold: 0.1
}
const allTheLazyImages = document.querySelectorAll('.image__list-lazy-image');
let observer;
// Does your browser support InersectionObserver?
if('IntersectionObserver' in window) {
observer = new IntersectionObserver(lazyLoader, options);
// select all our image which we want to have lazy loaded
allTheLazyImages.forEach(image => {
// put them in the observer
observer.observe(image);
});
} else {
allTheLazyImages.forEach(image => {
lazyLoadImage(image); // if it is not supported, load all
});
}
function lazyLoader (entries) {
// loop through all images
entries.forEach(entry => {
// does the viewport hit the current image
if (entry.intersectionRatio > 0) {
// yes! load the image
lazyLoadImage(entry.target);
}
});
}
function lazyLoadImage(observedImage) {
/**
* We hit an observed image!
* First, remove the lazy loading class. This will show the image
* Then remove the observer
*/
observedImage.classList.remove('image__list-lazy-image');
// If we have an background image, replace the source with the data-lazyBackground attribute
if(observedImage.dataset.lazybackground) {
observedImage.style.backgroundImage = `url(${observedImage.dataset.lazybackground})`;
}
// does it have a data attribute with image-src?
if (observedImage.getAttribute('data-src')) {
// yes! So lets make the image source equal to the data image source and render it!
observedImage.src = observedImage.dataset.src;
if('IntersectionObserver' in window) {
// now that the image has bee renderes, we don't need to observe it anymore
observer.unobserve(observedImage);
}
}
}
View Compiled
This Pen doesn't use any external CSS resources.