Loading Images Based on Screen Visibility
Lazy loading images is a technique that defers the loading of non-critical resources (such as images) until they are needed. This approach can significantly improve page load time and user experience, especially on pages with many images.
- HTML Structure: Images are placed within
<img>
tags with adata-src
attribute containing the URL of the image to be lazy-loaded. - JavaScript:
getViewportHeight()
: A function to get the viewport height.inView()
: Determines if an element is in view based on its position relative to the viewport.loadVisibleImages()
: Loads images that are in or near the viewport. It checks if an image is within 500 pixels of the viewport and loads it if it is.- Event listeners for
load
andscroll
events trigger theloadVisibleImages()
function to load images when the page loads or is scrolled.
Note
The code uses a manual approach to lazy loading images. With the IntersectionObserver
API, this manual method is obsolete, but it still might be used in various scenarios where IntersectionObserver
is not supported or when fine-grained control over lazy loading behaviour is needed.
The following script is a tiny, tiny, vanilla JavaScript lazy loading solution, weighing in at just 258
bytes gzipped (445
bytes uncompressed). It loads images only when they are about to come into view, reducing initial page load time and bandwidth usage. Did I say tiny?
Note that the threshold is intentionally set to a lower value so you can see the loading event.
<img src="px.png" data-src="pawel-czerwinski-Y4rs8BpjCm4-unsplash.jpg" alt="" width="1920" height="1280">
/**
* Get viewport height.
* @returns {number} Viewport height.
*/
const getViewportHeight = () =>
window.innerHeight || document.body.offsetHeight || 0;
/**
* Check if element is in view.
* @param {HTMLElement} elem - The element to check.
* @param {number} nearThreshold - Threshold for considering an element as in view.
* @returns {boolean} True if element is in view, false otherwise.
*/
const inView = (elem, nearThreshold = 0) => {
const viewportHeight = getViewportHeight();
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
const elemTop = elem.offsetTop;
const elemHeight = elem.offsetHeight;
return scrollTop + viewportHeight + nearThreshold > elemTop + elemHeight;
};
/**
* Load visible images.
*/
const loadVisibleImages = () => {
const images = document.querySelectorAll("img[data-src]");
images.forEach((img) => {
if (img.offsetParent !== null && inView(img, 500)) {
img.src = img.dataset.src;
img.removeAttribute("data-src");
img.addEventListener("load", () => img.removeAttribute("data-src"));
}
});
};
// Load visible images on page load and scroll
window.addEventListener("load", loadVisibleImages);
window.addEventListener("scroll", loadVisibleImages);