Reveal an element on scroll with Intersection Observer

Add a short, once-only entrance when an element reaches the viewport, with visible content by default and a reduced-motion fallback.

Live example

A little movement. Nothing more.

Replay a short entrance: the content stays readable before and after it.

A clear idea.
A small detail.
A nicer experience.

These three notes are the static example. Enable JavaScript to replay their entrance.

A little movement, without hiding the page

Use this for a decorative card or illustration that should make a small entrance as it comes into view. Keep important text and controls immediately readable. The demo runs locally in the browser; it does not load a plugin or send anything to a server.

Unlike a scroll handler that repeatedly measures every element, IntersectionObserver lets the browser notify you when an element crosses a visibility threshold.

Mark the elements

<div data-reveal>A card that is readable before JavaScript runs.</div>

The default styles should leave the element visible. Add the following to a deferred script:

function installReveals(root = document) {
  const preference = matchMedia('(prefers-reduced-motion: reduce)');
  if (preference.matches || !('IntersectionObserver' in window)
    || !('animate' in Element.prototype)) return () => {};

  const animations = new Set();
  const observer = new IntersectionObserver(entries => {
    for (const entry of entries) {
      if (!entry.isIntersecting) continue;
      observer.unobserve(entry.target);
      const animation = entry.target.animate([
        { opacity: 0.65, transform: 'translateY(10px)' },
        { opacity: 1, transform: 'translateY(0)' },
      ], { duration: 350, easing: 'ease-out' });
      animations.add(animation);
      animation.finished.catch(() => {}).finally(() => animations.delete(animation));
    }
  }, { threshold: 0.1 });

  const stop = () => {
    observer.disconnect();
    for (const animation of animations) animation.cancel();
    animations.clear();
  };
  const onPreference = () => { if (preference.matches) stop(); };
  preference.addEventListener('change', onPreference);
  root.querySelectorAll('[data-reveal]').forEach(element => observer.observe(element));

  return () => {
    stop();
    preference.removeEventListener('change', onPreference);
  };
}

const removeReveals = installReveals();
// In a client-routed application, call removeReveals() when this view unmounts.

Why this version is forgiving

There is no “hide everything until JS adds a class” rule. A blocked script, unsupported API or reduced-motion preference leaves the content untouched. Each observed element animates once, and changing the preference to reduced motion stops remaining effects.

Apply it to an inner wrapper if the component already uses transform; otherwise the animation temporarily replaces that transform. Avoid very tall observed elements that cannot reach the threshold, and do not make animations a prerequisite for reading, clicking or understanding the page.

References: Intersection Observer, Element.animate, and reduced motion.

Original version3 August 2013

Kept here for reference and earlier links. The updated guide above is the recommended starting point; older code may depend on retired services or different software versions.

This Code Byte provides a really nice way to fade in an object/image/text just as it's brought into the view of the user. It makes for an elegant and simple stylish reveal, though not to be overused.


var $win = $(window);
var $img = $('.fadeInScroll'); // Change this to affect your desired element.

$win.on('scroll', function () {
  var scrollTop = $win.scrollTop();

  $img.each(function () {
    var $self = $(this);
    var prev = $self.offset();
    if (prev) {
      var pt = 0;
      pt = prev.top - $win.height();
      $self.css({
        opacity: (scrollTop - pt) / ($self.offset().top - pt)
      });
    } else {
      $self.css({
        opacity: 1
      });
    }
  });

}).scroll();

So if you're looking for a way to show image on web browser scroll or any other item this is the one for you!

trulycode-responsive
Hello

Keep exploring

A couple more useful notes.