IntersectionObserver

by tomik23

HTML

<img width="400" height="400" data-src="https://place-hold.it/400x400/15252D/fff">
<img width="400" height="400" data-src="https://place-hold.it/400x400/15252D/fff">
<img width="400" height="400" data-src="https://place-hold.it/400x400/15252D/fff">
<img width="400" height="400" data-src="https://place-hold.it/400x400/15252D/fff">
<img width="400" height="400" data-src="https://place-hold.it/400x400/15252D/fff">
<img width="400" height="400" data-src="https://place-hold.it/400x400/15252D/fff">
<img width="400" height="400" data-src="https://place-hold.it/400x400/15252D/fff">

<h2>Picture Element</h2>
<picture>
  <source media="(min-width: 400px)" data-srcset="https://place-hold.it/400x400/15252D/fff">
  <source media="(min-width: 200px)" data-srcset="https://place-hold.it/200x200/15252D/fff">
  <img data-src="https://place-hold.it/100x100/15252D/fff">
</picture>

<picture>
  <source media="(min-width: 400px)" data-srcset="https://place-hold.it/400x400/15252D/fff">
  <source media="(min-width: 200px)" data-srcset="https://place-hold.it/200x200/15252D/fff">
  <img data-src="https://place-hold.it/100x100/15252D/fff">
</picture>

<picture>
  <source media="(min-width: 400px)" data-srcset="https://place-hold.it/400x400/15252D/fff">
  <source media="(min-width: 200px)" data-srcset="https://place-hold.it/200x200/15252D/fff">
  <img data-src="https://place-hold.it/100x100/15252D/fff">
</picture>

<picture>
  <source media="(min-width: 400px)" data-srcset="https://place-hold.it/400x400/15252D/fff">
  <source media="(min-width: 200px)" data-srcset="https://place-hold.it/200x200/15252D/fff">
  <img data-src="https://place-hold.it/100x100/15252D/fff">
</picture>

<picture>
  <source media="(min-width: 400px)" data-srcset="https://place-hold.it/400x400/15252D/fff">
  <source media="(min-width: 200px)" data-srcset="https://place-hold.it/200x200/15252D/fff">
  <img data-src="https://place-hold.it/100x100/15252D/fff">
</picture>

CSS

* {
  margin: 0;
  padding: 0;
}

html,
body {
  height: 100%;
}

body {
  width: 100%;
  background: #f2f2f2;
}

html {
  width: 100%;
  margin: auto;
  text-align: center;
}

.fade-in {
  animation-name: fadeIn;
  animation-duration: 300ms;
  animation-timing-function: cubic-bezier(0, 0, 0.4, 1);
  animation-fill-mode: forwards;
}

picture,
img {
  display: block;
  border: 2px solid red;
  margin-bottom: 2px;
  margin: auto;
}

@keyframes fadeIn {
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

@media (min-width: 200px) {
  picture {
    height: 200px;
    width: 200px;
  }
}

@media (min-width: 400px) {
  picture {
    height: 400px;
    width: 400px;
  }
}

JavaScript

let images = document.querySelectorAll('source, img');

if ('IntersectionObserver' in window) {
  // IntersectionObserver Supported
  let config = {
    root: null,
    rootMargin: '0px',
    threshold: 0.5
  };

  let observer = new IntersectionObserver(onChange, config);
  images.forEach(function(img) {
    observer.observe(img)
  });

  function onChange(changes, observer) {
    changes.forEach(function(change) {
      if (change.intersectionRatio > 0) {
        // Stop watching and load the image
        loadImage(change.target);
        observer.unobserve(change.target);
      }
    });
  }

} else {
  // IntersectionObserver NOT Supported
  images.forEach(function(image) {
    loadImage(image)
  });
}

function loadImage(image) {
  image.classList.add('fade-in');
  if (image.dataset && image.dataset.src) {
    image.src = image.dataset.src;
  }

  if (image.dataset && image.dataset.srcset) {
    image.srcset = image.dataset.srcset;
  }
}