Lazy Load Images with JavaScript

A website's page speed is one of the important factors when optimizing a website for performance and user experience. Lazy loading images plays a vital role in improving page speed with the image lazy load technique.

Lazy Load Images with JavaScript

Lazy loading images, sometimes known as image deferring, is an approach used to load an image only when it is in the viewport. With the lazy load image approach, we do not load off-screen images. This approach reduces the load time of a page and helps pages to load faster. In this post, we are going to learn how to lazy load images in JavaScript. We are going to create the following files for this post:

  • index.php: Our main HTML page containing the images for lazy loading.
  • javascript.js: The JavaScript code to implement lazy loading of images.
  • style.css: CSS styles for the whole HTML page.

How to Lazy Load Images in JavaScript (Step-by-Step)

Lazy loading images on websites gives a performance benefit in terms of loading images only when they are needed. It also reduces bandwidth waste and boosts Core Web Vitals performance. Lazy loading images using JavaScript can be achieved in the following steps:

  • Add necessary classes to images on the page to detect which images are to be lazy loaded.
  • Add data-src attribute instead of src to prevent immediate load.
  • Implement image lazy load with JavaScript, either the scroll event or the Intersection Observer.
 

Step 1: Add The Lazy Load Class to Images

Create an HTML page and render images in a loop.

  • Add the class lazy-image to each image on the page that is supposed to lazy load.
  • Add data-src, which will hold the source location of the image.
  • We will assign the src attribute to the image using this data-src attribute.

index.php

<!DOCTYPE html>
<html>
<head>
<title>Lazy Load Images with Javascript - Demo</title>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
<meta content="width=device-width, initial-scale=1, maximum-scale=1" name="viewport"/>
<link rel="stylesheet" href="css/style.css"/>
</head>
<body>
<section class="section py-4">
<div class="container">
<div>
<?php for ($i = 1; $i <= 12; $i++) { ?>
<span class="has-lazy-image">
<img class="image-responsive lazy-image" data-src="images/lazy-image-<?= $i; ?>.jpg"
alt="Lazy Load Image <?= $i; ?>"
title="Lazy Load Image <?= $i; ?>"
width="640" height="420"/>
</span>
<?php } ?>
</div>
</div>
</section>
<footer>
<script src="js/javascript.js" type="text/javascript"></script>
</footer>
</body>
</html>
 

Step 2.1: Lazy Load Images in JavaScript (On Scroll Event)

There are two ways we can lazy load images in JavaScript. We will cover both methods in this post; the first one is to load images on the scroll event. Steps to lazy load images in JavaScript:

  • Add a DOMContentLoaded event listener to the document.
  • Select all the images with the class lazy-image but not having the loaded class.
  • Create a function lazyLoad to add images to the lazy images array and add event listeners for DOMContentLoaded, orientationchange, scroll, and resize events.
  • Inside the function, loop through all images and check if the current image is in the viewport. Use the getBoundingClientRect() and compare it with the window's inner height.
  • Also, check if the image is currently not hidden; we do not want to show those images.
  • Assign the height attribute to the image using the value from data-height attribute.
  • Assign the src attribute using the value from data-src attribute.
  • Add the class loaded to the image.
  • Remove the image from the lazy images array.
  • Remove all event listeners for the lazyLoad function if there are no images left in the lazy images array.

javascript.js

document.addEventListener("DOMContentLoaded", function () {
let active = false;
let lazy_images = [].slice.call(document.querySelectorAll(".lazy-image:not(.loaded)"));

const lazy_load = function () {
if (active === false) {
active = true;

setTimeout(function () {
lazy_images.forEach(function (lazy_image) {
if ((lazy_image.getBoundingClientRect().top <= window.innerHeight && lazy_image.getBoundingClientRect().bottom >= 0)
&& getComputedStyle(lazy_image).display !== "none") {
lazy_image.style.height = lazy_image.dataset.height || null;

lazy_image.src = lazy_image.dataset.src;
lazy_image.classList.add("loaded");

lazy_image.removeAttribute("data-src");
lazy_image.removeAttribute("data-height");

lazy_images = lazy_images.filter(function (image) {
return image !== lazy_image;
});

if (lazy_images.length === 0) {
window.removeEventListener("scroll", lazy_load);
window.removeEventListener("resize", lazy_load);
window.removeEventListener("orientationchange", lazy_load);
}
}
});

active = false;
}, 200);
}
};

window.addEventListener("load", lazy_load);
window.addEventListener("scroll", lazy_load);
window.addEventListener("resize", lazy_load);
window.addEventListener("orientationchange", lazy_load);
});
 

Step 2.2: Lazy Load Images with Intersection Observer

One other approach to achieve image lazy load is to use JavaSript's Intersection Observer, which can be used considering the browser compatibility at the time of implementation. Steps to lazy load images with Intersection Observer:

  • Add a DOMContentLoaded event listener to the document.
  • Select all the images with the class lazy-image but not having the class loaded, and loop through all the images.
  • Check if the browser supports Intersection Observer.
  • Create an intersection observer for images.
  • Inside the intersection observer, check for each image entry if it is in the viewport.
  • Assign the src attribute using the value from data-src attribute.
  • Add the class loaded to the image.
  • Finally, outside the function we created, loop through all images and assign the intersection observer to each image.
document.addEventListener("DOMContentLoaded", function() {
var lazy_images = [].slice.call(document.querySelectorAll(".lazy-image:not(.loaded)"));

if ("IntersectionObserver" in window) {
let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
let lazy_image = entry.target;
lazy_image.src = lazyImage.dataset.src;
lazy_image.classList.add("loaded");
lazyImageObserver.unobserve(lazy_image);
}
});
},{rootMargin: "0px 0px -120px 0px"});

lazy_images.forEach(function(lazy_image) {
lazyImageObserver.observe(lazy_image);
});
}
});
 

Step 3: Add Lazy Loading Class to All Images

Manually adding the lazy-image class could be a lot to do. This useful block of code will make the job easier and programmatically add the lazy-image class to all existing images. Place this code right before the lazy load code.

(function(document){
let images = [].slice.call(document.querySelectorAll("img"));
images.forEach(function (image) {
image.dataset.height = image.style.height;
image.dataset.src = image.src;

image.src = "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzIDIiPjwvc3ZnPg==";
image.classList.add("lazy-image");

image.style.height = image.height + 'px';
});
})(document);
 

Add CSS Styles

Styles for our entire HTML user interface page and lazy images.

style.css

* {
box-sizing: border-box;
}
html,body {
margin: 0;
padding: 0;
}
body {
background-color: #f6f6f6;
font-family: "Segoe UI", "Roboto", "Helvetica", sans-serif;
font-size: 15px;
font-weight: normal;
font-style: normal;
line-height: 1.5;
}
.container {
width: 100%;
max-width: 1140px;
margin-right: auto;
margin-left: auto;
padding-right: 15px;
padding-left: 15px;
}
.py-4 {
padding-top: 1rem;
padding-bottom: 1rem;
}
.has-lazy-image {
display: inline-block;
border: 5px solid #fff;
margin-bottom: 1rem;
box-shadow: 0 0 5px rgba(0,0,0,0.5);
}
.column-left .lazy-image {
opacity: 0;
transition: opacity ease-in .2s;
}
.image-responsive {
display: block;
max-width: 100%;
height: auto;
}
.lazy-image.loaded {
opacity: 1;
}

Lazy loading is a simple technique that helps to improve a website's performance. We demonstrated two ways to lazy load images in JavaScript. Either of the methods can help reduce the initial page load time.