Build a Collapsible Menu in React with Intersection Observer

Shubham Khatri
JavaScript in Plain English
4 min readMar 30, 2021

--

When people talk about Intersection Observer, the most common use cases that come to mind are Lazy loading Images and Infinite Scroll. However Intersection Observer can be put to use in a lot more interactions.

In this post we are going to implement one such interaction, which is a collapsible menu, in which only the items that can take the available space will be shown upfront and rest will go inside the overflow menu.

Collapsible menu

Why 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. — developer.mozilla.org

For our design, IntersectionObserver can be a good fit because it can provide information about menu items that are overflowing the container (i.e. not visible within the container).

Let’s start!

Our analysis:

  • We need to know all elements that are present inside the container.
  • We also need information of which one’s are visible and which one’s are overflowing the container on resizing.
  • Once we have both the information we can just control the visibility of elements with CSS and render overflowing items inside the overflow menu.

First we start with an initial block of code that renders menu items as children in a container. For the purpose of this post, I am using Material-UI with static content but the same concept can be extended to any component library or custom components and dynamic content too.

Rendering our menu items as child elements to IntersectionObserverWrapper

Notice that we are passing a unique prop data-targetid to each child of IntersectionObserverWrapper, which will be used later on.

Now let’s dive into the core logic where we will implement the IntersectionObserverWrapper.

  • Create a new instance of IntersectionObserver.
  • Pass root element as the container of the child elements. (navRef.current in our example)
  • Set the threshold to 1. This means that, at any change in 100% of the target visibility our Observers callback will be executed.
  • In the observer callback we will update a state map to track visibility of elements.
  • After creating the Observer, we need to observe our targets i.e. menu items. For this, we get all the children of ancestor node which in our case is referenced with navRef and add as target only if it has a data-targetid property.
  • Both the logic of creating observer and adding targets can be added inside useEffect which runs post initial render.
  • Remember to disconnect the observer on unmount of the component.
Basic implementation of IntersectionObserverWrapper with IntersectionObserver setup

Now we are ready to handle the logic for maintaining visibility state (visibilityMap).

IntersectionObserver callback

Now that we have visibility state of each menu item, we can control their visibility during rendering by adding additional classNames using React.cloneElement(Yes, have to use React.cloneElement, can’t do it any other way for static children).The visibility can either be controlled by setting CSS property visibility or opacity.

Render menu items with additional className property based on visibilityMap

Our Implementation up till this point will look like this:

Hold on. Its not finished yet…

Let’s implement Overflow Menu

For overflow menu we need to pass down all menu items and their visibility state as props.

Note: We can choose to render the Overflow menu outside of our container in all conditions or inside of it so that it is right beside the last visible element instead of being at the end of container always. For this demo, I will add it inside the container since it has higher complexity than it being rendered outside of container.

Note that we haven’t passed the prop data-targetid to OverflowMenu component as we do not want our IntersectionObserver to observe on it.

The rendering logic of Overflow menu is simple, we filter and render only elements that are visible inside the menu

OverflowMenu rendering logic

Also the key to aligning the overflow menu to the right of last visible element is using flexbox with order property. The visible elements will have an order value less than that of overflow menu and the invisible elements will have the order value higher than it.

Our CSS styles

And that is everything you need to build a collapsible menu!

CodeSandbox Playground

Conclusion

We successfully made a collapsible menu in react. Our application only renders items that are visible within the container and the overflowing elements are rendered inside the overflow menu.

Take a look at the full implementation of the code here.

More content at plainenglish.io

--

--

Software Engineer at Meta | Passionate about Javascript, React and Web Development | Active Stackoverflow contributor