Responsive Image Lazyload

A reusable function to lazy load any element (not just image) on both vertical and horizontal scroll event. Just for fun!...

by Bhaumik Mehta

HTML

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<h3>Resize the window to see how both vertical and horizontal lazy loading happens</h3>
<div class="container flexbox">
	<div class="wrapper flexbox flex-1 dir-column">
		<h3 class="heading">Scroll down to lazy load images &Downarrow;</h3>
		<div id="images" class="images flexbox flex-1 dir-column">
			<a href="https://picsum.photos/id/42/400" target="_blank"><img class="lazy-load-image lazy-not-loaded" src="" data-src="https://picsum.photos/id/42/400" /></a>
			<a href="https://picsum.photos/id/237/400" target="_blank"><img class="lazy-load-image lazy-not-loaded" src="" data-src="https://picsum.photos/id/237/400" /></a>
			<a href="https://picsum.photos/id/1011/400" target="_blank"><img class="lazy-load-image lazy-not-loaded" src="" data-src="https://picsum.photos/id/1011/400" /></a>
			<a href="https://picsum.photos/id/1025/400" target="_blank"><img class="lazy-load-image lazy-not-loaded" src="" data-src="https://picsum.photos/id/1025/400" /></a>
			<a href="https://picsum.photos/id/1024/400" target="_blank"><img class="lazy-load-image lazy-not-loaded" src="" data-src="https://picsum.photos/id/1024/400" /></a>
			<a href="https://picsum.photos/id/349/400" target="_blank"><img class="lazy-load-image lazy-not-loaded" src="" data-src="https://picsum.photos/id/349/400" /></a>
			<a href="https://picsum.photos/id/1074/400" target="_blank"><img class="lazy-load-image lazy-not-loaded" src="" data-src="https://picsum.photos/id/1074/400" /></a>
			<a href="https://picsum.photos/id/164/400" target="_blank"><img class="lazy-load-image lazy-not-loaded" src="" data-src="https://picsum.photos/id/164/400" /></a>
			<a href="https://picsum.photos/id/175/400" target="_blank"><img class="lazy-load-image lazy-not-loaded" src="" data-src="https://picsum.photos/id/176/400" /></a>
			<a href="https://picsum.photos/id/180/400" target="_blank"><img class="lazy-load-image...

CSS

/*** Reset and Common Styles ***/
html, body {
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
  overflow: hidden;
}
h2, h3 {
    text-align: center;
    padding: .3em;
    margin: 0;
}
.flexbox {
  display: -webkit-flex;
  display: -moz-flex;
  display: -ms-flexbox;
  display: flex;
}
.flex-1 {
  -webkit-flex: 1;
  -moz-box-flex: 1;
  -ms-flex: 1;
  flex: 1;
}
.dir-column {
  flex-direction: column;
}

/*** Element Styles Start ***/
.heading {
	background-color: rgba(0,0,0,0.3);
  color: #222;
  text-shadow: 1px 1px 1px rgb(255, 255, 255, .2);
}
.container {
  height: calc(100% - 2rem);
}
.wrapper {
  height: 100%;
  max-width: 50%;
  margin-right: 1em;
	border: 1px solid #cdcdcd;
}
.images {
  align-items: center;
  overflow: hidden auto;
  min-height: 100px;
	border-top: 1px solid #cdcdcd;
	padding: .4rem;
	background-color: #f0f8ff;
}
.images a {
	pointer-events: none;
	min-width: 11rem;
	min-height: 11rem;
	padding: 0.5rem;
}
img.lazy-load-image {
  display: block;
	min-height: 11rem;
	max-width: 100%;
  max-height: 100%;
  border: 0;
  margin-bottom: 10px;
  opacity: 1;
  -webkit-transition: transform .2s;
  -moz-transition: transform .2s;
  -ms-transition: transform .2s;
  transition: transform .2s;
	object-fit: cover;
	filter: grayscale(90%);
	pointer-events: none;
	position: relative;
	background-color: rgba(0,0,0,0.4);
	box-shadow: 3px 2px 8px rgba(0, 0, 0, .6);
}
img.lazy-not-loaded:after {
		content: "\f03e";
    font-family: FontAwesome;
    font-size: inherit;
    text-rendering: auto;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    font-size: 4em;
    position: absolute;
    color: #222;
    width: 100%;
    height: 100%;
    left: 0;
    top: 0;
    display: flex;
    justify-content: center;
    align-items: center;
		z-index: 100;
		animation-name: loading;
		animation-duration: 1.5s;
		animation-fill-mode: forwards;
		animation-iteration-count: infinite;
		animation-direction:...

JavaScript

/******************************* 
		Author: Bhaumik Mehta
    Created on: 02/14/2021
		Language: JavaScript
********************************/

//-------------------------- Function starts ------------------------//

/* 
	1. This reusable function terminates by returning false, if the event target or nodes is undefined/null OR there are no nodes with "not loaded" custom class.
	2. Else, it iterates though all the not loaded nodes.
  3. This function gets object as an argument.
  3. This function can be used for vertical or horizontal scrolling lazy load.
*/
const lazyload = ({ container, nodes, isHorizontal, notLoadedClass="lazy-not-loaded", loadedClass="lazy-loaded" }) => {
	
  if (!container || !nodes || nodes.length === 0) {
    return false;
  }
  
  /*
  	1. Convert the images node array to an iterable array and then use map to iterate through each image.
  	2. If the class contains the not loaded class and the node is in viewport (Checking for class is important to make the "if" block runs only once for each image).
  	3. Set the source and alt if the node is image and remove the data-src.
  	4. Remove not loaded class and add loaded class, and return true as the map needs to return something.
  */
  Array.from(nodes).map(node => {
		if (node.classList.contains(notLoadedClass) && inViewport(node, container, isHorizontal)) {
      if (node.tagName === "IMG") {
        	node.src = node.dataset.src;
    			node.alt = node.dataset.src;
        	node.removeAttribute("data-src");
      }
    	node.classList.remove(notLoadedClass);
     	node.classList.add(loadedClass);
      return true;
    }
  });
};

/* 
	1. Helper function to calculate and return true/false based on if the node is in the viewport.
  2. If it's a horizontal scroll then check if the rect right is greater than zero and if the rect left minus left offset calculation is less than or equal to the container width.
	3. For the vertical operation check for the bottom and then if the rect top minus the...