ReactJS: Dropdown menus

Abinav Seelan
campvanilla

--

Web applications today rely heavily on dropdown menus to de-clutter the user interface — by stowing away additional non-critical options inside menus that you can trigger by hovering over or by clicking on an icon.

An example of a dropdown menu from Hashnode.com

This article is a quick guide on building dropdown menus like 👆.

We’ll be building a menu with the following interactions :-

  1. If you click on the icon, the menu appears.
  2. Clicking on anything inside the menu does not close the menu.
  3. Clicking anywhere outside the menu will close the menu.

The setup

The first thing we need is the component that houses this menu.

We have a Card component that has a button, as well as a div with class menu that will be our hidden dropdown menu, which right now is not really hidden is it?

Let’s fix that. We can set a state variable in the constructor to hold the current visible state of the menu.

We set this.state.showMenu to false by default since we want the menu to hidden by default. We have added a small check on line 20 to see what the value of this.state.showMenu is, and if it’s true, then we display the menu.

But… How do we toggle this.state.showMenu? 🤔

Showing the menu

Let’s add a click handler to the Show menu button to show the menu.

Let’s add a showMenu function to our component. Whenever this method is called, it sets this.state.showMenu to true. Once the value is set, the component is going to re-render and since it’s true now the menu will display

showMenu() seems to be working.

But there’s still a missing piece to the full feature.

If you notice at the end of ☝️, I click outside the menu but nothing happens. We need to find out a way to close the menu when we click outside the menu.

Hiding the menu

We know that everything on the page has a common parent — the document. One way that we can handle the interaction of clicking outside the menu to close it is by adding a click event listener to this common parent, so that we can click anywhere to close the menu.

When we call showMenu, let’s also add an event listener to document. Any time there is a click event to document, we should set this.state.showMenu to false.

And since this is a click event that we want to track only once, let’s also remove the event listener from document once we’re done.

We’ve added a new class method called closeMenu() that sets this.state.showMenu to false. And inside showMenu() on line 19, we add an event listener to the document to call closeMenu whenever it’s clicked. (Read more about the callback version of setState here.)

And since we want it to trigger only once, we also remove the event handler added to document inside closeMenu on line 25.

Now let’s see this in action!

It works! But wait….

And it seems to be working as expected! or at least … sort of.

While it does work the way we want, you’d notice at the end of ☝️, clicking on any of the menu items inside the menu closes the menu as well! This is something that we may not want.

How do we fix this? 🤔

Check for .contains()

A way around this is to check if the origin of our click event is from an element that the dropdown menu contains — basically, its children.

To do this, we first need a reference to the dropdown menu. We can use the ref property to get a reference to the DOM element:

Here, on lines 43–45, we use the ref property that React provides to set a class variable called this.dropdownMenu to point to the DOM reference of <div class="menu"> ... </div>.

Now that we have the reference, when we call closeMenu, let us check if the origin of the event, which we can access via event.target is a DOM node that is contained within the this.dropdownMenu element.

Line 25 is where we check if this.dropdownMenu contains the event.target. In other words, we check if the origin of the event was from within the menu itself, and only if it isn’t, do we set the state and remove the event handler from document.

Now, let’s see how this works!

Everything works!

And it works perfectly! 😄

Clicking on the “Show Menu” button renders the menu. Clicking on any button inside the menu no longer closes the menu, but clicking outside the menu closes it. 🎉

~Fin~

If you found this helpful, do leave a 👏!

Stuck somewhere, need more help, or just want to say hi? Send me a Direct Question on Hashnode or hit me up on Twitter. You can also find me on Github. 🙃

--

--