Basic Usage

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      console.log('Element is visible:', entry.target);
    }
  });
});

// Watch an element
const el = document.getElementById('my-element');
observer.observe(el);

// Stop watching
observer.unobserve(el);
observer.disconnect(); // stop all

Scroll Animations

/* CSS */
.fade-in {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 0.5s ease, transform 0.5s ease;
}
.fade-in.visible {
  opacity: 1;
  transform: translateY(0);
}
// JS — observe all .fade-in elements
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.classList.add('visible');
      observer.unobserve(entry.target); // animate only once
    }
  });
}, { threshold: 0.15 }); // trigger when 15% visible

document.querySelectorAll('.fade-in').forEach(el => observer.observe(el));

Lazy Loading Images

<!-- HTML: use data-src instead of src -->
<img data-src="/images/photo.jpg" alt="Photo" class="lazy"/>
const imageObserver = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      img.classList.remove('lazy');
      imageObserver.unobserve(img);
    }
  });
}, { rootMargin: '200px' }); // start loading 200px before visible

document.querySelectorAll('img.lazy').forEach(img => imageObserver.observe(img));
💡 Note: Modern browsers support the native loading="lazy" attribute on images, which is even simpler. Use IntersectionObserver when you need custom control over the loading behaviour.

Options Explained

const observer = new IntersectionObserver(callback, {
  root: null,           // null = viewport; or a scrollable element
  rootMargin: '0px',    // expand/shrink root box (like CSS margin)
  threshold: 0.5        // 0=any pixel visible, 1=fully visible, 0.5=half visible
                        // Array: [0, 0.25, 0.5, 0.75, 1] fires at each point
});