We would normally track if a DOM element is visible in the viewport or not to understand user behaviour or to operate on the elment based on visibilty (animation, ajax calls etc).

We would normally apply a function like below to detect if an element is visible in the viewport:

1 2 const isInView = ( elm , { top , height } = elm . getBoundingClientRect ()) => top <= innerHeight && top + height >= 0 ;

Later we would mostly add it on scroll events to trigger the checks as the user scroll, which in turn is a costly operation.

That's where the Intersection Observer API comes into rescue.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 // From the spec const observer = new IntersectionObserver ( changes => { for ( const change of changes ) { console . log ( change . time ); // Timestamp when the change occurred console . log ( change . rootBounds ); // Unclipped area of root console . log ( change . boundingClientRect ); // target.boundingClientRect() console . log ( change . intersectionRect ); // boundingClientRect, clipped by its containing block ancestors, and intersected with rootBounds console . log ( change . intersectionRatio ); // Ratio of intersectionRect area to boundingClientRect area console . log ( change . target ); // the Element target } }, {}); // Watch for intersection. observer . observe ( target ); // Stop watching. observer . unobserve ( target ); // Stop observing. observer . disconnect ();

Say, if we were to observe this element, #catBox for visiblity, we would end up with: