Lazy Image Loading (Using Intersection Observer API [including polyfill])

by Konstantin Rouda

HTML

<main>

  <p class="o-text">
  I do not find it easy to get sufficiently far away from this
Book, in the first sensations of having finished it, to refer
to it with the composure which this formal heading would
seem to require. My interest in it, is so recent and strong;
and my mind is so divided between pleasure and regret -
pleasure in the achievement of a long design, regret in the
separation from many companions - that I am in danger of
wearying the reader whom I love, with personal confidences,
and private emotions.
Besides which, all that I could say of the Story, to any
purpose, I have endeavoured to say in it.
It would concern the reader little, perhaps, to know, how
sorrowfully the pen is laid down at the close of a two-years’
imaginative task; or how an Author feels as if he were dismissing
some portion of himself into the shadowy world,
when a crowd of the creatures of his brain are going from
him for ever. Yet, I have nothing else to tell; unless, indeed,
I were to confess (which might be of less moment still) that
no one can ever believe this Narrative, in the reading, more
than I have believed it in the writing.   Instead of looking back, therefore, I will look forward. I
cannot close this Volume more agreeably to myself, than
with a hopeful glance towards the time when I shall again
put forth my two green leaves once a month, and with a faithful remembrance of the genial sun and showers that
have fallen on these leaves of David Copperfield, and made
me happy. I REMARKED in the original Preface to this Book, that I
did not find it easy to get sufficiently far away from it, in
the first sensations of having finished it, to refer to it with
the composure which this formal heading would seem to
require. My interest in it was so recent and strong, and my
mind was so divided between pleasure and regret - pleasure
in the achievement of a long design, regret in the separation
from many companions - that I was in danger of wearying
the reader with personal...

CSS

HTML {
  /*using system font-stack*/
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
  font-size: 115%; /*~18px*/
  line-height: 1.5;
  box-sizing: border-box;
}

BODY {
  margin: 0;
  color: #3a3d40;
  background: rgb(254, 255, 255);
}

*, *::before, *::after {
  box-sizing: inherit;
  color: inherit;
}

IMG { max-width: 100%; }

/*Actual Style*/
MAIN { 
  max-width: 90vw;
  margin: 0 auto;
  padding: 1.5rem .1rem;
}

SECTION {
  max-width: 950px;
  margin: 0 auto 3rem;
  //border: 1px solid;
}

/*C-Aspect Ratio Box*/

.c-img-box {
  position: relative;
  margin: 0 1.5rem 1.5rem;
}

.c-img-box--aspect-ratio {
  background-color: rgb(220, 220, 220);
  background: linear-gradient(-37deg, transparent 50%, rgb(188, 188, 188) 50.3%, transparent 50.7%), linear-gradient(37deg, transparent 50%, rgb(188, 188, 188) 50.3%, transparent 50.7%) rgb(220, 220, 220);
}

.c-img-box--aspect-ratio::before,
.c-img-box--aspect-ratio::after {
  content: "";
}

.c-img-box--aspect-ratio::before {
   float: left;
   width: 1px; height: 0;
   margin-left: -1px;
   padding-top: 75%;
}

.c-img-box--aspect-ratio::after {
  display: table;
  clear: both;
}

/*.c-aspect-ratio-box__content {
  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 100%;
  background: rgba(200, 200, 200, 1);
}*/


.o-text {
  font-size: 2rem;
  margin: 0 0 10rem;
}

JavaScript

;(function() {
  "use strict";
  
  const imgPlaceholders = document.querySelectorAll("[data-img-src]");
  
  const lastImg = imgPlaceholders[imgPlaceholders.length - 1];
  
  const IMG_SRC_ATTR = "data-img-src";
  const IMG_CONTAINER_PLACEHOLDER_CLASS = "c-img-box--aspect-ratio";
  
  const observerOptions = {
 // root: null,
 // rootMaring: "10rem",
    threshold: 0.25 // when visibility passes the N% mark, i.e. when observer element is visible within its parent on N% at least.
  };
  
  /// Initialize Observer object
  const observer = new IntersectionObserver(handleIntersection, observerOptions);
   
  imgPlaceholders.forEach(function(img) { 
      /// Start observing
      observer.observe(img);   
  });
  
  
  /*IntersectionObserver Handler*/
  function handleIntersection(entries, observerObj) {

    entries.forEach(function(entry, i) {
      
      if(!entry.isIntersecting) return; /// the target is no longer as visible as the given threshold
      
      const img = entry.target; /// the real HTML Element
      const srcVal = img.getAttribute(IMG_SRC_ATTR);
      
       if(srcVal) {
         img.src = srcVal; // replace fake src to the original one
         img.onload = function() { 
           this.removeAttribute(IMG_SRC_ATTR); 
           this.parentElement.classList.remove(IMG_CONTAINER_PLACEHOLDER_CLASS);
         };
       }
      
       if(img === lastImg) {
          // disconnect observer
          observerObj.disconnect();
       }
      
    });
    
  };
  
  
})();
  

/*
LINKS:

https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API

polyfill:
https://github.com/WICG/IntersectionObserver/tree/gh-pages/polyfill

*/