Guide

Intersection Detection

Intersection detection means detecting whether a DOM element intersects with the viewport. By viewport, we mean the currently viewable portion of the website.

To detect an intersection, we either need to

  1. Subscribe to the scroll event.
  2. Use Intersection Observer API

Differences between these two approaches are outlined in IO VS Scroll.

onscroll event is the old way of detecting intersections. It runs on the main thread of the web browser and if not implemented properly, it can severely affect the performance of your site. But it has the ability to constantly check for interaction information, unlike the intersection observer.

Intersection observer is a newly defined API for this task. And it has good support in most browsers(Check here). But there are cases where intersection observer cannot be used. For example, in continuous scroll detection, you cannot use this. Instead, these cases require the old onscroll event to handle them.

Detecting using scroll events

1. Add Sensor

To make the sensor available, wrap your contents with the ScrollSense component. Ideally, you can put this to the root level of your component hierarchy.


import ScrollSensor from 'react-scrollsense';

function App() {

     return (
          <ScrollSensor config=>
               <MyAwesomeComponent/>
          </ScrollSensor>
     )
}

export default App;

ScrollSense takes a configuration object called config with following.

Property Description
delay Scroll events are throttled for performance reasons. The throttling interval can be set by delay.
rootMargin This sets a global offset which will be reduced from your component bounds. This is a string with either pixel or percentage components for top, right, bottom, and left. Examples :
</br><ul><li>‘10px 20px 30px 10px’ means, top offset is 10%. right 20px. bottom 30px and left 10%.</li><li>‘0 20px’ means top and bottom are 0px while right and left are 20px.</li><li>30 - all directions have 30px.</li>

root for this sensor is the browser window viewport.

2. Connect your components

Now you can start adding scroll detection to your components. To do that, you can use the useScrollSense hook.

import {useScrollSense}  from 'react-scrollsense';
import {useEffect, useRef} from 'react';

function MyAwesomeComponent() {
     
     const sensor = useScrollSense();
     const ref = useRef();
     const [cls, setCls] = useState('my-component');

     useEffect(() => {

          let tracker = sensor.onIntersection(ref.current, (entry, el) => {

               if(entry.isIntersecting) {
                    // Now its on screen let's change class
                    setCls('my-component scrolled');
               }
               else {
                    // It's off screen
                    setCls('my-component');
               }

          });

          return () => { sensor.detach(tracker)}

     }, [sensor]);

     return (
          <div className={cls} ref={ref}>
               Hello, Scroll Me!
          </div>
     )

}

export default MyAwesomeComponent;

useScrollSense hook returns an object with onIntersection and detach methods.

Use the returned onIntersection method to add your behavior when an intersection is detected.

onIntersection is called whenever your component intersects with the viewport. It needs a ref to your component and a callback which you can use to add your own logic when an intersection happens.

Make sure when you call this onIntersection method, it is supplied with a valid ref argument. Calling this method inside useEffect is the recommended way to do this.

You can use the detach method to stop listening to intersection events. Calling this method will detach your component from the sensor provider.

Detecting using Intersection Observers

1. Add Sensor

To use the intersection observer API sensor you need to import from the react-scrollsense/io module.

import ScrollSensor from 'react-scrollsense/io';

function App() {

     return (
          <ScrollSensor config=>
               <MyAwesomeComponent/>
          </ScrollSensor>
     )
}

export default App;

This ScrollSense accepts prop called config, with following

Property Description
root The DOM element or document whose bounding rectangle is used to check for the created intersection observer. See here for more info.
threshold Amount of distance that your component should interact with the root to trigger an intersection event. This can be a floating number in the range [0,1] or an array of floating numbers in the range [0,1]. If it’s a single value then it’s considered a single detection point. If it’s an array, we consider that as multiple detection points. See here for more info.
rootMargin Same as above section on scroll events.

Additional notes on advanced use for threshold:

Intersection observer API only accepts floating numbers in the range [0, 1]. Although this library allows you to provide percentages and pixels as specified below, they are internally converted to the above range for the API.

Although, IO API only allows [0,1], you can use string percentage values. For example, [0, ‘5%’, ‘10%’, ‘20%’]. When we provide percentage values, they are calculated into floating numbers between 0 - 1 internally by library. So internally this is equal to [0, 0.05, 0.1, 0.2].

Also, if you are planning to use only the mode which uses a dedicated intersection observer per component (useScrollSense() or useScrollSense(true)), then you can also specify pixel values (as string) for threshold. For example : [20px, 50px]. The library will internally generate the percentage values using the width and height of the elements. But if you specify pixels for threshold values, you CAN NOT use any component that uses the single intersection observer mode (useScrollSense(false)). In this case, the threshold value will be set to 0.5. This limitation exists because all elements are sharing a single io.

2. Connect your component to sensor

There are two ways to use intersection observer-based detection.

One way is to use a single observer for all components. This will only create a single observer in the sensor provider.

The other way is to use an observer per component. This will create a dedicated observer for each component you connect with the sensor provider.

import {useScrollSense}  from 'react-scrollsense/io';
import {useEffect, useRef} from 'react';

function MyAwesomeComponent() {
     
     const sensor = useScrollSense();
     const ref = useRef();
     const [cls, setCls] = useState('my-component');

     useEffect(() => {

          let tracker = sensor.onIntersection(ref.current, (entry, el) => {

               if(entry.isIntersecting) {
                    // Now its on screen let's change class
                    setCls('my-component scrolled');
               }
               else {
                    // It's off screen
                    setCls('my-component');
               }

          }, {
            theshold: 0.2, //override provider threshold,
            rootMargin: '10px' //override provider root margin.
          });

          return () => { sensor.detach(tracker)}

     }, [sensor]);

     return (
          <div className={cls} ref={ref}>
               Hello, Scroll Me!
          </div>
     )

}

export default MyAwesomeComponent;

useScrollSense accepts a single boolean parameter called, useMultipleIOs. Default is true if you don’t pass it.

Setting it to true will create a dedicated intersection observer for your component.

If you set it to false, then a single intersection observer will handle all your components.

This returns an object which contains onIntersection and detach methods.

Use the onIntersection method to add your behavior when an intersection is detected.

Make sure when you call this returned onIntersection method, it is supplied with a valid ref argument. Calling this method inside useEffect is the recommended way to do this.

Use the detach method to unsubscribe from the sensor. You can use the return function of the useEffect hook to unsubscribe when the component gets unmounted.

Examples

While its up to your imagination and requirements to decide what to do upon detecting an intersection, following are some of the common tasks you will find this library useful.

1. Adding fade-in effect

Let’s say that you need to fade in your components as the user scrolls down. Here is a quick example of how to do that. Here I use scroll events.

Since here we are using translate transform to move the lines vertically, it’s better to avoid the io-based sensor (or detach after the first intersection). As pointed out in IO VS Scroll events section, if you do repeated translate transforms or animations, it’s better to use the default sensor instead of the intersection observer-based sensor.

2. Change class on intersection

If you need to change element contents or their CSS classes when a component gets into the view, the following example may help you.

3. Animation on scroll

When you need to create animations, it’s better to use the scroll-based sensor. It will give you continuous updates on an intersection.


Copyright © 2023. Kasun Jayawardena (XPD). Distributed by MIT license.