Lazy Image Loading Reveal
How to Create a Lazy Image Loading Reveal with Vanilla JavaScript
Watch the full tutorial here:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="./app.js" defer></script>
<link rel="stylesheet" href="./style.css">
<title>Document</title>
</head>
<body>
<div id="img-container">
<div class="img-wrapper">
<img src="./img/blur-img/imagen-low-01.png" class="preview" width="300" height="200">
<img data-src="https://images.unsplash.com/photo-1748701821466-0b9f8bf839ac?q=80&w=1451&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
class="real" loading="lazy" width="300" height="200">
</div>
<div class="img-wrapper">
<img src="./img/blur-img/imagen-low-02.png" class="preview" width="300" height="200">
<img data-src="https://images.unsplash.com/photo-1596833504535-ca1a5a4b5c57?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1yZWxhdGVkfDF8fHxlbnwwfHx8fHw%3D"
class="real" loading="lazy" width="300" height="200">
</div>
<div class="img-wrapper">
<img src="./img/blur-img/imagen-low-03.png" class="preview" width="300" height="200">
<img data-src="https://images.unsplash.com/photo-1621825347931-41fe02769c29?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1yZWxhdGVkfDV8fHxlbnwwfHx8fHw%3D"
class="real" loading="lazy" width="300" height="200">
</div>
<div class="img-wrapper">
<img src="./img/blur-img/imagen-low-04.png" class="preview" width="300" height="200">
<img data-src="https://images.unsplash.com/photo-1593194634277-ba2834f5dcbb?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1yZWxhdGVkfDZ8fHxlbnwwfHx8fHw%3D"
class="real" loading="lazy" width="300" height="200">
</div>
<div class="img-wrapper">
<img src="./img/blur-img/imagen-low-05.png" class="preview" width="300" height="200">
<img data-src="https://images.unsplash.com/photo-1642046268189-c53f2903b9c9?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1yZWxhdGVkfDE1fHx8ZW58MHx8fHx8"
class="real" loading="lazy" width="300" height="200">
</div>
</div>
</body>
</html>
css
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
#img-container {
width: 100%;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
gap: 5px;
padding: 5px;
& .img-wrapper {
width: 300px;
height: 200px;
position: relative;
overflow: hidden;
& img {
width: 100%;
display: block;
height: auto;
object-fit: cover;
}
& .preview {
filter: blur(20px);
position: absolute;
top: 0;
left: 0;
z-index: 1;
transition: opacity 0.3s ease;
}
& .real {
position: relative;
z-index: 2;
opacity: 0;
transition: opacity 0.3s ease;
}
& .real.loaded {
opacity: 1;
}
}
}
javascript
document.querySelectorAll('.real').forEach(img => {
const realSrc = img.dataset.src;
const fullImg = new Image();
fullImg.src = realSrc;
fullImg.onload = () => {
img.src = realSrc;
img.classList.add('loaded');
const wrapper = img.closest('.img-wrapper');
const preview = wrapper.querySelector('.preview');
if (preview) {
preview.style.opacity = '0';
setTimeout(() => {
preview.remove();
}, 300);
};
};
});