Getting started with Intersection Observer
Table of contents
When developing an interface, there are several request that most often are demanded, like providing animations or effects to the elements on the screen when they enter (and also exit) the viewport. Obviously these effects should be triggered when the elements are “visible” in order to enrich the user experience, if they are not, the will lose their value.
Calculating their position on the screen
We used to proceeded selecting the node in the DOM and calculating its position in the viewport or within any container listening the scroll
event, getting its coordinates, the height of the window and the scroll position at each moment. With this approach, we could guess whether or not an element was being visible and acted in consequence. For example, we could add or remove a class name. The strategy was more or less like this 👇
const target = document.querySelector('.element')
document.addEventListener('scroll', () => {
const { top, height } = target.getBoundingClientRect()
if (top < window.innerHeight && top + height > 0) {
// Do some stuff...
}
})
But when following this approach, we encounter a new issue. Should we throttle
the scroll
event?. It is not always so clear, it depends. Perhaps you are doing this once and feels like it is not necessary, we could avoid loading more dependencies to the project such as third party libraries like lodash or adding more complexity with your own implementation.
Some events are fired many times in a short period of time, such asscroll
, fired constantly when the user scrolls the page. We can optimize this situation avoiding the callback to be fired withthrottle
, which it is, basically, a way to stop the execution of the function for a given time.
Intersection Observer
Today, using IntersectionObserver
is the safest, easiest and most optimized way to get to know where an element is in a page. In addition, we do not have to tackle with the scroll.
IntersectionObserver
is a JavaScript API that let us constantly observe an element. So, we could know when it enters or exits a certain area, being the watchable reference the document or a container. Then, if the user scrolls down or up or a resize event gets fired, we will be notified whether the situation of our target has changed.
It is very useful for guessing the section a user is watching and reacting to this, loading more images, some content or adding or removing styles to perform entering or exiting animations. Useful for adding more interactivity to our sites enriching the user experience.
A tipical case
const callback = (entries, observer) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
// Do something...
}
})
}
const options = {}
const observer = new IntersectionObserver(callback, options)
let target = document.querySelector('.element')
observer.observe(target)
First things first. We define an options object for shaping the observer (now is empty), then we instantiate an observer using IntersectionObserver
, we select the target we want to observe (DOM node) and finally make the observer observe the target.
In addition to the options object (optional), the observer expects a function in which we check whatever condition and make some stuff. In the example above, we evaluate if the element is inside the observable area.
Each entry is an IntersectionObserverEntry. The basic operation is checking the value of the boolean isIntersecting
but it also contains more useful information.
IntersectionObserverEntry {
time: 149248.54,
rootBounds: DOMRect,
boundingClientRect: DOMRect,
intersectionRect: DOMRect,
isIntersecting: true,
intersectionRatio: 0.04942339373970346,
target: article.main-page-content
}
How to change when the callback is fired
const options = {
root: document.querySelector('#container'),
rootMargin: '0px',
threshold: 1.0,
}
The options object fine-tune over the observer behavior and it defines when the callback will be executed.
root
defines the observable area. Is our element of reference to check if the target gets into it. If none element is declared ornull
it will bedocument
by default.rootMargin
is an option to modify the area ofroot
. It is used for spanning or cropping the area. It is very similar to themargin
property (top, right, bottom and left) and it acceptspx
as units as well as percentages. ArootMagin
declare as"10px 0px -10px 0px"
means that the area will being cropped10px
inset.threshold
is a number or an array of numbers between 0.0 and 1.0 that indicate the visibility percentage the target should be present in order to fire the callback. Its value is 0.0 by default, so the function will be triggered once the very first pixel of the target element crosses the observable area. However, if we use 1.0 we expect the hole element to be within the observable area. If we pass-in an array of numbers, we will fire the callback once for each value it meets.
Troubleshooting
Sometimes, most often the first times you play around with IntersectionObserver
happens that it does not work as we were expecting. In these situations, double check this tips.
root
should be the element we want to stablish the observable area. If we does not declare it (it will benull
) and if we are working inside aniframe
it will not match. We must usedocument
.- Double check the values given to
rootMargin
and make sure they make sense. Do not collapsetop
andbottom
because the result will be unpredictable. - If the element is higher than the observable area and the
threshold
is1.0
your callback will not be launched since the element will not be entirely visible within the observable area.
Multiple references
Observing multiple references at the same time is easy. You just need to iterate the collection of elements (using forEach
for example) for indicating to each of them that it will be observed by the observer. Nevertheless, is the same function for all these elements.
Accessibility
If you are planning to add effects when the elements enter or exit an area, just remember that what is attractive and funny for someone could be annoying for another one, so maybe you could make an incremental feature or at least deactivate them if the user has this preference settled.
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
Conclusion
IntersectionObserver
is the easiest and more optimal way to control when an element is within a visible area in a page or section, and no matter if you find troubles when getting started, it is worths it.
Nowadays, the browsers are working for implementing the new features of the V2, which improve the current version, mostly the well known issues regarding to isVisible
, a property of the IntersectionObserverEntry
which is always false
, so everything points out to be an API that will have its continue in the future.
Resources
comments powered by DisqusIf you find it interesting
If you have any doubt or you want to talk about this topic, if the content or our profiles are interesting to you and you think we could something together, do not hesitate to contact us on twitter or trough the email address hola@mamutlove.com