Script Valley
Building Your Developer Portfolio
Adding Interactivity with JavaScriptLesson 3.3

How to add scroll-triggered animations to a portfolio using IntersectionObserver

IntersectionObserver API, threshold, rootMargin, fade-in animation, CSS opacity and transform, observe and unobserve, performance vs scroll events

Animate on Scroll Without Performance Issues

Scroll-triggered animations make a portfolio feel polished. The correct tool is IntersectionObserver — not scroll event listeners, which fire hundreds of times per second and kill performance.

CSS: Hidden Initial State

.fade-in {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 0.5s ease, transform 0.5s ease;
}

.fade-in.visible {
  opacity: 1;
  transform: translateY(0);
}

JavaScript: IntersectionObserver

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.classList.add('visible');
      observer.unobserve(entry.target); // Animate once only
    }
  });
}, { threshold: 0.15 });

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

Add class="fade-in" to section headings and project cards. The threshold: 0.15 option triggers the animation when 15% of the element is visible.

Call observer.unobserve() after adding the visible class so the observer stops watching elements that have already animated. This avoids unnecessary callbacks and keeps the observer lean. Always respect prefers-reduced-motion by wrapping the observer setup in a motion check.

Up next

How to build a working contact form with JavaScript validation

Sign in to track progress