Search code examples
javascriptinternet-explorerintersection-observer

Lazy loading img's with IntersectionObserver polyfill fails in Internet Explorer


I'm attempting to implement lazy loading of images using IntersectionObserver where available, and by using a polyfill otherwise (as recommended here).

+function ($, window, document, undefined) {
    var lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));

var lazyLoadImage = function() {
    var lazyImageObserver = new IntersectionObserver(function(entries, observer) {
        entries.forEach(function(entry) {
            if (entry.isIntersecting) {
                var lazyImage = entry.target;
                lazyImage.src = lazyImage.dataset.src;
                lazyImage.classList.remove("lazy");
                lazyImageObserver.unobserve(lazyImage);
            }
        });
    });

    lazyImages.forEach(function(lazyImage) {
        lazyImageObserver.observe(lazyImage);
    });
};

var lazyLoadImagePolyfill = function() {
    var active = false;

    if (active === false) {
        active = true;

        setTimeout(function() {
            lazyImages.forEach(function(lazyImage) {
                if ((lazyImage.getBoundingClientRect().top <= window.innerHeight
                     && lazyImage.getBoundingClientRect().bottom >= 0)
                     && getComputedStyle(lazyImage).display !== 'none') {
                         console.log('lazyImage:', lazyImage);
                         lazyImage.src = lazyImage.dataset.src;
                         lazyImage.classList.remove('lazy');

                         lazyImages = lazyImages.filter(function(image) {
                             return image !== lazyImage;
                         });

                        if (lazyImages.length === 0) {
                            document.removeEventListener('scroll', lazyLoadImagePolyfill);
                            window.removeEventListener('resize', lazyLoadImagePolyfill);
                            window.removeEventListener('orientationchange', 
                            lazyLoadImagePolyfill);
                    }
                }
            });
            active = false;
        }, 200);
    }
    document.addEventListener("scroll", lazyLoadImagePolyfill);
    window.addEventListener("resize", lazyLoadImagePolyfill);
    window.addEventListener("orientationchange", lazyLoadImagePolyfill);
};

document.addEventListener('DOMContentLoaded', function(){
    if ("IntersectionObserver" in window) {
        lazyLoadImage();
    } else {
        lazyLoadImagePolyfill();
    }
});
}(jQuery, window, document)

This approach has worked correctly in all browsers I've tested, except IE. I'm getting a SCRIPT5007: Unable to get property 'src' of undefined or null reference in the console; the line that's throwing the error is lazyImage.src = lazyImage.dataset.src;. However, the console.log preceding that line shows the following:

lazyImage: [object HTMLImageElement]
    "lazyImage:"
    <img class="lazy" src="placeholder.png" data-src="real-pic.jpg"></img>

Please note: I've been asked to not make use of an external library or plugin. Any ideas why this is happening and how to correct it?


Solution

  • I usually use in-view for this sort of things whenever I cannot use observers, but you said that you've been asked not to use any external library or plugin, so I've written you this code in vanilla javascript. This code will run on IE9=< without polyfills.

    Note: Please take into account that I did not make any optimizations of performance, for example, say in case all images have been loaded remove event listener and such.

    document.addEventListener('DOMContentLoaded', lazyLoadImagePolyfill);
    
    function lazyLoadImagePolyfill() {
      var lazyLoadImages = Array.from(document.getElementsByClassName('lazy'));
      _loadImage();
      window.onscroll = _loadImage;
    
      function _loadImage() {
        for (let i = 0; i < lazyLoadImages.length; i += 1) {
          var img = lazyLoadImages[i];
          if (isVisible(img)) {
            showImage(img);
          }
        }
      }
    }
    
    function isVisible(element) {
      var {
        top,
        bottom
      } = element.getBoundingClientRect();
      var vHeight = (window.innerHeight || document.documentElement.clientHeight);
      return ((top > 0 || bottom > 0) && top < vHeight);
    }
    
    function showImage(img) {
      var src = img.getAttribute('data-src');
      if (!src) {
        return;
      }
      img.setAttribute('src', src);
      img.onload = function() {
        img.removeAttribute('data-src');
      };
    }
    img {
      margin-bottom: 200px;
      min-height: 1px;
      width: 100%;
      display: block;
    }
    
    .container {
      display: flex;
      flex-wrap: wrap;
      width: 400px;
      margin: auto;
    }
    <div class="container">
      <img class="lazy" data-src="http://www.facetheforce.today/han-old/400" alt="Han Solo Old">
      <img class="lazy" data-src="http://www.facetheforce.today/han/400" alt="Han Solo">
      <img class="lazy" data-src="http://www.facetheforce.today/luke-old/400" alt="Luke Old">
      <img class="lazy" data-src="http://www.facetheforce.today/luke/400" alt="Luke">
      <img class="lazy" data-src="http://www.facetheforce.today/han-old/400">
    </div>