Skip to content

Instantly share code, notes, and snippets.

@Snugug
Last active February 12, 2018 22:29
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Snugug/a8a57d2fb72d93a0bba79638eef98928 to your computer and use it in GitHub Desktop.
Save Snugug/a8a57d2fb72d93a0bba79638eef98928 to your computer and use it in GitHub Desktop.
Progressively Enhanced Lazy Loading Images in 40 lines of code

Lazy Load Images in 40 Lines of Code

Behold! Intersection Observers at work!

Usage

First, update your source code to move your image sources in to data attributes.

<!-- Image tags get `src` -->
<img data-src="foo/bar.png" alt="Foo Bar Image" />
<noscript>
  <img src="foo/bar.png" alt="Foo Bar Image" />
</noscript>

<!-- Picture tags get `srcset` and `src` -->
<picture>
  <source data-srcset="foo/bar.webp" type="image/webp">
  <source data-srcset="foo/bar.png" type="image/png">
  <img data-src="foo/bar.png" alt="Foo Bar Image" />
</picture>
<noscript>
  <picture>
    <source srcset="foo/bar.webp" type="image/webp">
    <source srcset="foo/bar.png" type="image/png">
    <img src="foo/bar.png" alt="Foo Bar Image" />
  </picture>
</noscript>

Then, when your DOM is loaded, run the lazyload function!

document.addEventListener('DOMContentLoaded', () => {
  lazyload();  
});

What's Happening

  • Lazyload grabs all of the picture and img tags
  • If IntersectionObserver exists, use it to set up lazyloading! If not, just load all of the images in
  • When an image has at least 1px in the viewport, it'll load the images in
function loadImage(elem) {
if (elem.tagName === 'PICTURE') {
const sets = elem.querySelectorAll('[data-srcset]');
const srcs = elem.querySelectorAll('[data-src]');
sets.forEach(set => {
set.srcset = set.dataset.srcset;
delete set.dataset.srcset;
});
srcs.forEach(src => {
src.src = src.dataset.src;
delete src.dataset.src;
});
} else if (elem.tagName === 'IMG') {
if (elem.dataset.src) {
elem.src = elem.dataset.src;
delete elem.dataset.src;
}
}
}
function loadImageObserver(entries, observer) {
entries.forEach(entry => {
if (entry.isIntersecting) {
loadImage(entry.target);
}
});
}
function lazyload() {
const images = document.querySelectorAll('picture', 'img');
if ('IntersectionObserver' in window) {
const observer = new IntersectionObserver(loadImageObserver);
images.forEach(image => observer.observe(image));
} else {
images.forEach(img => loadImage(img));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment