Intersection Observer
Reusable Intersection Observer for lazy loading and animations.
A comprehensive Intersection Observer utility class for lazy loading images, scroll animations, infinite scroll, and visibility tracking. Reusable and efficient with multiple observer management.
intersection-observer.jsjavascript
/**
* Intersection Observer Utility
* For lazy loading, animations, and scroll-triggered effects
*/
class IntersectionObserverManager {
constructor(options = {}) {
this.defaultOptions = {
root: null,
rootMargin: '50px',
threshold: 0.1,
...options
};
this.observers = new Map();
}
/**
* Observe elements with callback
*/
observe(elements, callback, options = {}) {
const observerOptions = { ...this.defaultOptions, ...options };
const observerKey = JSON.stringify(observerOptions);
if (!this.observers.has(observerKey)) {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
callback(entry.target, entry);
}
});
}, observerOptions);
this.observers.set(observerKey, observer);
}
const observer = this.observers.get(observerKey);
const elementList = elements instanceof NodeList ? Array.from(elements) :
Array.isArray(elements) ? elements : [elements];
elementList.forEach(el => observer.observe(el));
return observer;
}
/**
* Lazy load images
*/
lazyLoadImages(selector = 'img[data-src]') {
const images = document.querySelectorAll(selector);
this.observe(images, (img) => {
if (img.dataset.src) {
img.src = img.dataset.src;
img.removeAttribute('data-src');
img.classList.add('loaded');
}
if (img.dataset.srcset) {
img.srcset = img.dataset.srcset;
img.removeAttribute('data-srcset');
}
}, { rootMargin: '100px' });
}
/**
* Trigger animations on scroll
*/
animateOnScroll(selector = '[data-animate]', animationClass = 'animate-in') {
const elements = document.querySelectorAll(selector);
this.observe(elements, (el, entry) => {
const animation = el.dataset.animate || animationClass;
el.classList.add(animation);
// Unobserve after animation
if (el.dataset.animateOnce !== 'false') {
entry.target.observer?.unobserve(el);
}
}, { threshold: 0.2 });
}
/**
* Infinite scroll
*/
infiniteScroll(triggerElement, callback) {
this.observe(triggerElement, () => {
callback();
}, { rootMargin: '200px' });
}
/**
* Track element visibility
*/
trackVisibility(elements, onVisible, onHidden) {
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
onVisible?.(entry.target, entry);
} else {
onHidden?.(entry.target, entry);
}
});
}, this.defaultOptions);
const elementList = Array.isArray(elements) ? elements : [elements];
elementList.forEach(el => observer.observe(el));
return observer;
}
/**
* Disconnect all observers
*/
disconnectAll() {
this.observers.forEach(observer => observer.disconnect());
this.observers.clear();
}
}
// Usage Examples:
const observerManager = new IntersectionObserverManager();
// Lazy load images
observerManager.lazyLoadImages();
// Animate elements on scroll
observerManager.animateOnScroll('[data-fade-in]', 'fade-in');
// Infinite scroll
const loadMoreTrigger = document.querySelector('#load-more');
observerManager.infiniteScroll(loadMoreTrigger, () => {
console.log('Load more content...');
// Fetch and append more content
});
// Track visibility
const videoElement = document.querySelector('video');
observerManager.trackVisibility(
videoElement,
(el) => el.play(),
(el) => el.pause()
);
export default IntersectionObserverManager;Usage
Import and use: const observer = new IntersectionObserverManager(); observer.lazyLoadImages();
Let’s Build Something You’ll Be Proud Of
No fluff. Just thoughtful design and reliable development.
Work with me
Average response time: within 24 hours
