← Back toMaster of React ref

Detect whether an element is in view

Written byPhuoc Nguyen
Created
19 Oct, 2023
In our previous post, we introduced the `usePrevious()` hook, which helps us keep track of the previous value of a state. But did you know that this hook can also be used to detect when an element comes into view while scrolling? This is a powerful tool for creating engaging user experiences that respond to how users interact with your website.
When it comes to web development, detecting whether an element is in view can be incredibly useful. For example, you can use this technique to lazy-load images or videos, which greatly improves page performance by reducing the amount of data that needs to be loaded on page load. Additionally, you can use this technique for analytics tracking, such as tracking how far users scroll down a page or which sections of your website are getting the most attention.
But that's not all! You can also use this information to trigger animations. Imagine animating a section of your website when the user scrolls down and that section comes into view – it adds a dynamic and visually appealing aspect to your website.
In this post, we'll show you how to achieve this functionality using the `usePrevious()` hook.

Checking if an element is visible while scrolling

If you're wondering whether an element on your webpage is visible while you're scrolling, you can use the `getBoundingClientRect()` method. This method tells us about an element's size and position relative to the viewport.
To check if an element is visible, we compare the top and bottom edges of the element with the viewport's height. If the top edge is less than the viewport's height and the bottom edge is greater than 0, then we know the element is visible.
Here's a code snippet to help you out:
js
const rect = ele.getBoundingClientRect();
const isInView = rect.top < window.innerHeight &&
rect.bottom >= 0;
To get started, create a new reference for your element using the `useRef()` method. This reference allows you to easily access and manipulate your element within your code.
tsx
const eleRef = React.useRef(null);

// Render
return (
<div ref={eleRef}>...</div>
);
We're creating a new state variable called `isInView` to keep track of whether our element is currently visible on the screen. By default, it's set to `false`.
ts
const [isInView, setIsInView] = React.useState(false);
Next, we define a function called `checkInView()` that checks whether our element is currently visible on the screen by getting its size and position using `getBoundingClientRect()`. If it is visible, we set our `isInView` state variable to `true`. If not, we set it to `false`.
ts
const checkInView = () => {
const rect = eleRef.current.getBoundingClientRect();
setIsInView(
rect.top < window.innerHeight && rect.bottom >= 0
);
};
To initialize our `isInView` state variable, we add an effect hook that calls `checkInView()` once when our component mounts. And to ensure that our component is listening for the `scroll` event, we add another effect hook that adds an event listener for the `scroll` event and removes it when our component unmounts.
ts
React.useEffect(() => {
checkInView();
}, []);

React.useEffect(() => {
document.addEventListener("scroll", checkInView);
return () => {
document.removeEventListener("scroll", checkInView);
};
}, []);
Now comes the interesting part. Remember the `usePrevious()` hook we created in the previous post? We're going to use it now to retrieve the previous value of our `isInView` state variable. Then, we'll add another effect hook that checks whether our element has just come into view by comparing the current value of `isInView` with the previous value. If it has, we can do something cool with the element.
ts
const wasInView = usePrevious(isInView);

React.useEffect(() => {
if (!wasInView && isInView) {
// Element has come into view
// Do something with the element ...
console.log("Element is in view");
}
}, [isInView]);
With this technique, you can easily detect when an element comes into view while scrolling and take additional actions accordingly. Exciting, right? Let's dive into the next section to see how we can apply this technique to a real-life use case.
Good to know
Apart from handling the `scroll` event, there are other ways to check if an element is in view, like using the Intersection Observer API. We'll cover that approach in another series, so stay tuned!

Triggering animations when an element appears on screen

Let's say you have a website showcasing different types of products, each represented by a card with an image and some information. To make the site more engaging, you want to add an animation that fades in and scales the product when the user scrolls to the corresponding card.
To achieve this, you can modify the code example above. Instead of a `console.log` statement, you can add your animation function to be triggered when the element comes into view.
Here's an example: we add the CSS class `card__animated` to each card when it becomes visible.
ts
React.useEffect(() => {
if (!wasInView && isInView) {
eleRef.current.add('card__animated');
}
}, [isInView]);
At first, each card on the page is given the `card` class. This class is used to style the card element, which usually includes a product image and some information. Initially, the card is invisible with an opacity of 0. To improve performance during animation, the `will-change` property is used to let the browser know that the `transform` and `opacity` properties will change.
To prepare for animation, the `transform` property is used to move the card offscreen by 4rem (about two-thirds of its height) and scale it down to 80% of its size. This creates space for the card to be animated back into view later.
Finally, the `transition` property is used to animate any changes to the `transform` and `opacity` properties over a period of 400ms (0.4 seconds).
This is how we style the `card` class:
css
.card {
opacity: 0;
will-change: transform, opacity;
transform: translateY(4rem) scale(0.8);
transition: all 400ms;
}
To trigger our animation, we add the `.card__animated` class to our card element. This class does two things: it sets the opacity of our card to 1 and scales it up to its original size, all while transitioning smoothly onto the screen with the `translateY(0)` function.
The animation increases the card's opacity and smoothly moves it from offscreen to its final position on the page.
This is what its declarations look like:
css
.card__animated {
opacity: 1;
transform: translateY(0) scale(1);
}
By adding this animation, we can create a more engaging user experience that grabs the attention of our customers and encourages them to interact with our website.

Demo

Check out the final demo below! It includes 40 cards, each representing a different product. For demonstration purposes, we are only displaying the index of each product.

Conclusion

Detecting when an element is in view while scrolling can greatly improve your website's user experience. Not only does it make your site load faster by reducing the amount of data that needs to load at once, but it also lets you track user behavior and create cool animations based on how users interact with your site.
With the `usePrevious()` hook and `getBoundingClientRect()` method, it's easy to check if an element is visible while scrolling and take action accordingly. Whether you're loading images as users scroll or triggering animations, this technique adds a dynamic and visually appealing aspect to your website.
So why not give it a try? Experiment with different use cases and see how you can make your website stand out with this powerful tool.
If you found this post helpful, please consider giving the repository a star on GitHub or sharing the post on your favorite social networks 😍. Your support would mean a lot to me!

Questions? 🙋

Do you have any questions about front-end development? If so, feel free to create a new issue on GitHub using the button below. I'm happy to help with any topic you'd like to learn more about, even beyond what's covered in this post.
While I have a long list of upcoming topics, I'm always eager to prioritize your questions and ideas for future content. Let's learn and grow together! Sharing knowledge is the best way to elevate ourselves 🥷.
Ask me questions

Recent posts ⚡

Newsletter 🔔

If you're into front-end technologies and you want to see more of the content I'm creating, then you might want to consider subscribing to my newsletter.
By subscribing, you'll be the first to know about new articles, products, and exclusive promotions.
Don't worry, I won't spam you. And if you ever change your mind, you can unsubscribe at any time.
Phước Nguyễn