Find non Lazy Loaded Images outside of the viewport
List all images that don't have loading="lazy" or [data-src] (lazy loading via JS) and are not in the viewport when the page loads. This script will help you find candidates for lazy loading by showing:
- Total number of images without lazy loading
- Total file size of all images
- Individual image details: URL, resolution (naturalWidth x naturalHeight), and file size
Snippet
// Execute it after the page has loaded without any user interaction (Scroll, click, etc)
function isInViewport(tag) {
let rect = tag.getBoundingClientRect();
return (
rect.bottom >= 0 &&
rect.right >= 0 &&
rect.top <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.left <= (window.innerWidth || document.documentElement.clientWidth)
);
}
async function getImageSize(imgElement) {
const src = imgElement.currentSrc || imgElement.src;
if (!src || src.startsWith("data:")) {
return 0;
}
try {
const response = await fetch(src, { method: "HEAD" });
const contentLength = response.headers.get("content-length");
return contentLength ? parseInt(contentLength, 10) : 0;
} catch (error) {
console.warn(`Could not fetch size for: ${src}`, error);
return 0;
}
}
function formatBytes(bytes) {
if (bytes === 0) return "0 Bytes";
const k = 1024;
const sizes = ["Bytes", "KB", "MB", "GB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + " " + sizes[i];
}
async function findImgCandidatesForLazyLoading() {
let notLazyImages = document.querySelectorAll('img:not([data-src]):not([loading="lazy"])');
const notLazyImagesOutOfViewport = Array.from(notLazyImages).filter((tag) => !isInViewport(tag));
if (notLazyImagesOutOfViewport.length === 0) {
console.log(
`%c Good job, the site has all the images out of the viewport with lazyloading.`,
"background: #222; color: lightgreen; padding: 0.5ch",
);
return;
}
console.log(
`%c ⚠️ Found ${notLazyImagesOutOfViewport.length} images without lazy loading`,
"background: #222; color: lightcoral; padding: 0.5ch; margin-top: 1em; font-weight: bold",
);
// Collect information from images
const imagePromises = notLazyImagesOutOfViewport.map(async (img) => {
const src = img.currentSrc || img.src;
const size = await getImageSize(img);
const width = img.naturalWidth;
const height = img.naturalHeight;
return { element: img, src, size, width, height };
});
const imagesData = await Promise.all(imagePromises);
const totalSize = imagesData.reduce((sum, img) => sum + img.size, 0);
// Mostrar resumen
console.log(
`%c 📊 Summary:`,
"background: #222; color: #58a6ff; padding: 0.5ch; margin-top: 1em; font-weight: bold",
);
console.log(` • Total images: ${notLazyImagesOutOfViewport.length}`);
console.log(` • Total size: ${formatBytes(totalSize)}`);
// Display list of URLs with sizes and resolution
console.log(
`%c 📋 Image URLs:`,
"background: #222; color: #58a6ff; padding: 0.5ch; margin-top: 1em; font-weight: bold",
);
imagesData.forEach((img, index) => {
const sizeInfo = img.size > 0 ? `${formatBytes(img.size)}` : "size unknown";
const resolution = `${img.width}x${img.height}`;
console.log(` ${index + 1}. ${img.src} (${resolution}, ${sizeInfo})`);
});
// Display items in console (for inspection)
console.log(
`%c 🔍 Image elements (click or hover to inspect):`,
"background: #222; color: #58a6ff; padding: 0.5ch; margin-top: 1em; font-weight: bold",
);
notLazyImagesOutOfViewport.forEach((img) => console.log(img));
}
findImgCandidatesForLazyLoading();