Master Image Lazy Loading: From Scroll Events to IntersectionObserver
This article explains why loading all images in a long list hurts performance, presents step‑by‑step lazy‑loading techniques—including placeholder images, scroll calculations, getBoundingClientRect, and the modern IntersectionObserver API—provides full code samples, and discusses optimization tips for smoother front‑end experiences.
Problem Statement
In a page with many list items each containing an image, loading all images at once slows rendering and wastes bandwidth, especially when only the first few are visible. Lazy loading loads images only when they enter the viewport.
Implementation Approach
1. Load a placeholder image by default. 2. Listen to the container’s scroll event. 3. Calculate the visible area and trigger loading when an image is about to appear. 4. Replace the src attribute with the real image URL stored in data-src.
Key Concepts
scrollTop – the container’s scroll offset. offsetTop – distance from the element to its nearest positioned ancestor. offsetHeight – element’s height including padding and border. An image should be loaded when scrollTop + offsetHeight >= offsetTop - offset.
Basic Code Example
/* CSS */
.container{
width:200px;
height:200px;
position:relative;
overflow-y:scroll;
}
.img-area{
width:100px;
height:100px;
} <!-- HTML -->
<div class="container">
<div class="img-area">
<img class="pic" alt="loading"
data-src="./img/img1.png"
src="image-placeholder-logo.svg">
</div>
<!-- repeat img-area for other images -->
</div> // JavaScript
var container = document.querySelector('.container');
container.onscroll = function(){
checkImgs();
};
function isInSight(el){
var sTop = container.scrollTop;
var oHeight = container.offsetHeight;
var oTop = el.offsetTop;
return sTop + oHeight >= oTop;
}
function checkImgs(){
var imgs = document.querySelectorAll('.pic');
Array.from(imgs).forEach(function(el){
if(isInSight(el)){
loadImg(el);
}
});
}
function loadImg(el){
var source = el.dataset.src;
el.src = source;
}
checkImgs();Optimizations
Throttle the scroll handler to reduce calls.
Mark already‑loaded images to avoid repeated checks.
Pre‑load with an offset and apply a fade‑in effect.
Alternative Using getBoundingClientRect
By comparing el.getBoundingClientRect().top with window.innerHeight, an image is considered visible when top <= innerHeight. This method avoids manual offset calculations.
IntersectionObserver Solution
The IntersectionObserver API automatically notifies when an element enters or leaves the viewport, eliminating manual scroll calculations. It is supported in modern browsers (Chrome 51+, Edge 15, Android 5+) and can be polyfilled for older versions.
var observer = new IntersectionObserver(function(entries){
entries.forEach(function(entry){
if(entry.isIntersecting){
loadImg(entry.target);
}
});
});
document.querySelectorAll('.pic').forEach(function(img){
observer.observe(img);
});Infinite Scroll with IntersectionObserver
Observe a loading sentinel at the page bottom; when it becomes visible, fetch and prepend new items, then continue observing.
Conclusion
Image lazy loading significantly improves front‑end performance. Choosing between manual scroll calculations, getBoundingClientRect, or IntersectionObserver depends on project requirements and browser support.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Tencent IMWeb Frontend Team
IMWeb Frontend Community gathering frontend development enthusiasts. Follow us for refined live courses by top experts, cutting‑edge technical posts, and to sharpen your frontend skills.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
