Intersection Observer
The Intersection Observer API provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document's viewport.
Intersection information is typically used for:
- Lazy-loading of images or other content as a page is scrolled.
- Implementing "infinite scrolling" websites, where more and more content is loaded and rendered as you scroll so the user doesn't have to flip through pages.
- Reporting of visibility of advertisements to calculate ad revenues.
- Deciding whether or not to perform tasks or animation processes based on whether or not the user will see the result.
The Intersection Observer API lets you register a callback function executed whenever an element you wish to monitor enters or exits into another element or the viewport.
The callback you register is called when:
- The first time the observer is initially asked to watch a target element.
- A target element intersects either the device's viewport or a specified element. That specified element is called the root element or root.
Typically, you'll want to watch for intersection changes with the element's closest scrollable ancestor or, if the element isn't a descendant of a scrollable element, the viewport. To watch for intersections relative to the root element, specify null.
const options = {
root: null
}
Whether you're using the viewport or some other element as the root, the API works the same way, executing a callback function you provide whenever the visibility of the target element changes so that it crosses desired amounts of intersection with the root.
The intersection ratio is the degree of intersection between the target element and its root. This represents the percentage of the target element visible in the root as a value between 0.0 and 1.0.
Creating an intersection observer
Create the intersection observer by calling its constructor and passing it a callback function to be run whenever a threshold is crossed in one direction or the other:
const options = {
root: document.querySelector('#scrollArea'),
rootMargin: '0px',
threshold: 1.0
}
const callback = (entries, observer) => {
entries.forEach(entry => {
// do something
})
}
const observer = new IntersectionObserver(callback, options)
A threshold of 1.0 means that when 100% of the target is visible within the element specified by the root option, the callback is invoked.
Intersection observer options
The options object passed into the IntersectionObserver
constructor lets
you control the circumstances under which the observer's callback is
invoked. It has the following fields:
root
The element is used as the viewport for checking the target's visibility. It must be the ancestor of the target. Defaults to the browser viewport if not specified or if null.
rootMargin
Margin around the root. It can have values similar to the CSS margin property, e.g., "10px 20px 30px 40px" (top, right, bottom, left). The values can be percentages or pixels. This set of values serves to grow or shrink each side of the root element's bounding box before computing intersections—defaults to all zeros.
By default, the bounding box is the same size as the root element; a positive rootMargin will make the bounding box bigger than the root element’s size, and a negative rootMargin will make the bounding box shrink smaller than the root element.
threshold
A single number or an array of numbers indicates the percentage of the target's visibility the observer's callback should be executed.
- The default is 0 (meaning as soon as even one pixel is visible, the callback will be run).
- A value of 1.0 means that the threshold is only considered passed once every pixel is visible.
- If you only want to detect when visibility passes the 50% mark, you can use a value of 0.5.
- If you want the callback to run every time visibility passes another 25%,
you will specify the array
[0, 0.25, 0.5, 0.75, 1]
.
Targeting an element to be observed
Once you have created the observer, you need to give it a target element to watch:
const target = document.querySelector('#target);
observer.observe(target);
The callback receives a list of IntersectionObserverEntry
objects and the
observer object itself:
const callback = (entries, observer) => {
entries.forEach(entry => {
// Each entry describes a change for one observed target
entry.boundingClientRect
entry.intersectionRatio
entry.intersectionRect
entry.isIntersecting
entry.rootBounds
entry.target
entry.time
})
}
Check the value of the isIntersecting
property to see if the entry
represents an element that currently intersects with the root.
intersectionCallback(entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
let elem = entry.target;
if (entry.intersectionRatio >= 0.75) {
intersectionCounter++;
}
}
})
}
Intersection Observer Methods
IntersectionObserver.disconnect()
Stops theIntersectionObserver
object from observing any target.IntersectionObserver.observe(target)
Tells theIntersectionObserver
to start observing a particular target element.IntersectionObserver.takeRecords()
Returns an array ofIntersectionObserverEntry
objects for all observed targets.IntersectionObserver.unobserve(target)
Tells theIntersectionObserver
to stop observing a particular target element.
Summary
That's a wrap folks. You can easily implement infinite scrolling, lazy
loading or animations using the IntersectionObserver
API.
Resources
Here are some of the resources that inspired this note: