Lazy Load Images with Javascript
A website's page speed is an important factor that should be taken into consideration when optimizing a website. Lazy loading images plays a vital role in improving page speed with image lazy load technique.

Lazy loading images sometimes known as image defer loading is an approach used to load an image only when it is in viewport. With 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 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 whole HTML page.
How to Lazy Load Images in JavaScript (Step-by-Step)
Lazy loading images in websites gives 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 following steps:
- Add necessary classes to images on page to detect which images are to lazy load.
- Add
data-srcattribute instead ofsrcto prevent immediate load. - Implement image lazy load with JavaScript, either scroll event or intersection observer.
Step 1: Add Lazy Load Class to Images
Create an HTML page and render images in a loop.
- Add class
lazy-imageto each image on page that is supposed to lazy load. - Add
data-srcwhich will hold the source location of image. - we will assign
srcattribute to 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, first one is to load images on scroll event. Steps to lazy load images in javascript:
- Add a
DOMContentLoadedevent listener to document. - Select all the images with class
lazy-imagebut not having theloadedclass. - Create a function lazyLoad to lazy load images in lazy images array and add event listeners for
DOMContentLoaded,orientationchange,scrollandresizeevent. - Inside function loop through all images and check if current image in viewport using
getBoundingClientRect()and comparing it with window's inner height. - Also check if image is currently not hidden we do not want to show those images.
- Assign
heightattribute to image using value from data-height attribute. - Assign
srcattribute using value fromdata-srcattribute. - Add class
loadedto image. - Remove the image from lazy images array.
- Remove all event listeners for
lazyLoadfunction if there are no images left in 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 javascript'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 document. - Select all the images with class
lazy-imagebut not having the classloadedand loop through all the images. - Check if browser supports intersection observer.
- Create intersection observer for images.
- Inside intersection observer check if for each image entry if it is in viewport.
- Assign
srcattribute using value fromdata-srcattribute. - Add class
loadedto 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
If you feel it is a lot to do, to manually add lazy-image class to all existing images for lazy load. This block of useful code will make the job easier for you. Place this code right before lazy load code, This code will add lazy-image class to all of images.
(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;
}