<img src='https://via.placeholder.com/300' data-src='https://picsum.photos/id/237/300/300' alt='Imagen Cool 1' />
<img src='https://via.placeholder.com/300' data-src='https://picsum.photos/id/238/300/300' alt='Imagen Cool 2' />
<img src='https://via.placeholder.com/300' data-src='https://picsum.photos/id/239/300/300' alt='Imagen Cool 3' />
<img src='https://via.placeholder.com/300' data-src='https://picsum.photos/id/240/300/300' alt='Imagen Cool 4' />
<img src='https://via.placeholder.com/300' data-src='https://picsum.photos/id/241/300/300' alt='Imagen Cool 5' />
<img src='https://via.placeholder.com/300' data-src='https://picsum.photos/id/242/300/300' alt='Imagen Cool 6' />
<img src='https://via.placeholder.com/300' data-src='https://picsum.photos/id/243/300/300' alt='Imagen Cool 7' />
body {
  background-color: #eee;
}

img {
  width: 300px;
  height: 300px;
  display: block;
  margin: 0 auto 20px;
}
const callback = (entries, observer) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      // Tomamos el valor de data-src y lo seteamos en
      // el atributo src
      entry.target.src = entry.target.dataset.src;

      // Como la imagen cargará una vez, dejamos de observar el elemento.
      observer.unobserve(entry.target);
    }
  });
};

const options = {
  root: null, // Será el viewport. Esta línea la podemos omitir.
  rootMargin: '0px 0px -100px 0px', // Cuando la imagen esté a 100px de aparecer, ejecuto el callback.

  // Nota que no estoy seteando el threshold, por lo que el callback se ejecutará en cuanto el elemento empiece a ser visible (incluyendo el margen).
};

const observer = new IntersectionObserver(callback, options);
document.querySelectorAll('img').forEach((img) => {
  observer.observe(img);
});
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.