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