Detect whether an element is in view

phuocng

Phuoc Nguyen

Posted on December 10, 2023

Detect whether an element is in view

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:

const rect = ele.getBoundingClientRect();
const isInView = rect.top < window.innerHeight &&
                rect.bottom >= 0;
Enter fullscreen mode Exit fullscreen mode

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.

const eleRef = React.useRef(null);

// Render
return (
    <div ref={eleRef}>...</div>
);
Enter fullscreen mode Exit fullscreen mode

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.

const [isInView, setIsInView] = React.useState(false);
Enter fullscreen mode Exit fullscreen mode

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.

const checkInView = () => {
    const rect = eleRef.current.getBoundingClientRect();
    setIsInView(
        rect.top < window.innerHeight && rect.bottom >= 0
    );
};
Enter fullscreen mode Exit fullscreen mode

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.

React.useEffect(() => {
    checkInView();
}, []);

React.useEffect(() => {
    document.addEventListener("scroll", checkInView);
    return () => {
        document.removeEventListener("scroll", checkInView);
    };
}, []);
Enter fullscreen mode Exit fullscreen mode

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.

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]);
Enter fullscreen mode Exit fullscreen mode

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.

React.useEffect(() => {
    if (!wasInView && isInView) {
        eleRef.current.add('card__animated');
    }
}, [isInView]);
Enter fullscreen mode Exit fullscreen mode

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:

.card {
    opacity: 0;
    will-change: transform, opacity;
    transform: translateY(4rem) scale(0.8);
    transition: all 400ms;
}
Enter fullscreen mode Exit fullscreen mode

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:

.card__animated {
    opacity: 1;
    transform: translateY(0) scale(1);
}
Enter fullscreen mode Exit fullscreen mode

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.


It's highly recommended that you visit the original post to play with the interactive demos.

If you found this series 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!

If you want more helpful content like this, feel free to follow me:

πŸ’– πŸ’ͺ πŸ™… 🚩
phuocng
Phuoc Nguyen

Posted on December 10, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related