Smooth fade in effect for lazyloaded images
Using the loading="lazy"
attribute on <img>
is good for pagespeed performance. But the images will load in abruptly. For this specific case I use the following simple script and css in my projects.
Typescript
/**
* Body class .no-js is removed. This is needed for some stuff that depends on js.
*/
document.body.classList.remove('no-js');
/**
* Adds a "loaded" class to all images once they are fully loaded.
*
* This function:
* - Immediately adds the "loaded" class to images that are already loaded
* (e.g., from cache or preloading).
* - Attaches an "onload" event listener to images that are not yet loaded,
* ensuring the class is added as soon as they finish loading.
* - Works seamlessly with lazy-loaded images (e.g., those using the `loading="lazy"` attribute).
*
* Usage:
* Include this script in your project to apply a "loaded" class to all <img> elements
* for styling or further manipulation once the images are ready.
*/
function imgLoaded(): void {
// Helper function to handle individual image loading
const handleImage = (img: HTMLImageElement): void => {
if (img.complete) {
img.classList.add("loaded");
} else {
img.onload = (): void => {
img.classList.add("loaded");
};
img.onerror = (): void => {
console.error(`Failed to load image: ${img.src}`);
};
}
};
// Handle initial images
document.addEventListener("DOMContentLoaded", () => {
document.querySelectorAll("img").forEach(handleImage);
});
// Set up a MutationObserver to watch for new images and attribute changes
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
(mutation.target as Element).querySelectorAll("img")?.forEach(handleImage);
});
});
// Start observing the entire document for added nodes
observer.observe(document.body, {
childList: true,
subtree: true
});
}
imgLoaded();
CSS
Simple css ruleset that only applies if JS has been run. It's as simple as that.
body:not(.no-js) {
img[loading="lazy"] {
opacity: 0;
transition: opacity 0.2s ease-in-out;
&.loaded {
opacity: 1;
}
}
}
Smooth!