Skip to content

Instantly share code, notes, and snippets.

@kyleridolfo
Forked from Snugug/README.md
Created February 12, 2018 19:45
Show Gist options
  • Save kyleridolfo/52f980de040658ec21223f8d1835f16d to your computer and use it in GitHub Desktop.
Save kyleridolfo/52f980de040658ec21223f8d1835f16d 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