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 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-srcattribute instead ofsrcto 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-imageto 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
srcattribute 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
DOMContentLoadedevent listener to the document. - Select all the images with the class
lazy-imagebut not having theloadedclass. - Create a function lazyLoad to add images to the lazy images array and add event listeners for
DOMContentLoaded,orientationchange,scroll, andresizeevents. - 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
heightattribute to the image using the value from data-height attribute. - Assign the
srcattribute using the value fromdata-srcattribute. - Add the class
loadedto the image. - Remove the image from the lazy images array.
- Remove all event listeners for the
lazyLoadfunction 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
DOMContentLoadedevent listener to the document. - Select all the images with the class
lazy-imagebut not having the classloaded, 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
srcattribute using the value fromdata-srcattribute. - Add the class
loadedto 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.