Camkode
Camkode

Close Dropdown Menu on Click Outside

Posted by Kosal

Close Dropdown Menu on Click Outside

Dropdowns are a feature common to many websites. It's very useful, as they make it easy to show additional data only when it is needed. Dropdown menus allow you to make a web app clean and intuitive.

In this article, we will cover 2 examples:

  1. Using Functional Component
  2. Using Class Component

Within index.js, we call the App component. We define this component in App.js:

In App.css

.menu {
  position: relative;
  float: left;
}
.dropdown {
  position: absolute;
  width: 200px;
  border: 1px solid #ccc;
}

Using Functional Component

// In `App.js`
import { useState, useEffect, useRef } from "react";
import "./App.css";

function App() {
  // Create a ref for the element to detect outside clicks
  const ref = useRef();
  // Our dropdown's state
  const [isOpenDropdown, setOpenDropdown] = useState(false);
  // Call hook passing in the ref and a function to call on outside click
  useEffect(() => {
    const handleClickOutside = (event) => {
      // Do nothing if clicking ref's element or descendent elements
      if (ref && !ref.current.contains(event.target) && isOpenDropdown) {
        setOpenDropdown(false);
      }
    };
    // When user click any place in document
    document.addEventListener("click", handleClickOutside);
    return () => {
      // Clean it on rerender
      document.removeEventListener("click", handleClickOutside);
    };
  }, [ref, isOpenDropdown, setOpenDropdown]);
  return (
    <div ref={ref} className="menu">
      <button
        type="button"
        onClick={(e) => {
          e.preventDefault();
          setOpenDropdown(!isOpenDropdown);
        }}
      >
        Open menu
      </button>
      {isOpenDropdown && (
        <div className="dropdown">
          <ul>
            <li>Copy</li>
            <li>Paste</li>
            <li>Delete</li>
          </ul>
        </div>
      )}
    </div>
  );
}

export default App;

Using Class Component

// In `App.js`
import { Component, createRef } from "react";
import "./App.css";

class App extends Component {
  // Create a ref for the element to detect outside clicks
  ref = createRef();
  // Our dropdown's state
  state = {
    isOpenDropdown: false,
  };

  constructor(props) {
    super(props);
    this.handleClickOutside = this.handleClickOutside.bind(this);
  }

  componentDidMount() {
    // Call hook passing in the ref and a function to call on outside click
    document.addEventListener("click", this.handleClickOutside);
  }

  componentWillUnmount() {
    // Clean it on rerender
    document.removeEventListener("click", this.handleClickOutside);
  }

  handleClickOutside(event) {
    // Do nothing if clicking ref's element or descendent elements
    const { isOpenDropdown } = this.state;
    if (
      this.ref &&
      !this.ref.current.contains(event.target) &&
      isOpenDropdown
    ) {
      this.setState({ isOpenDropdown: false });
    }
  }
  render() {
    const { isOpenDropdown } = this.state;
    return (
      <div ref={this.ref} className="menu">
        <button
          type="button"
          onClick={(e) => {
            e.preventDefault();
            this.setState({ isOpenDropdown: !isOpenDropdown });
          }}
        >
          Open menu
        </button>
        {isOpenDropdown && (
          <div className="dropdown">
            <ul>
              <li>Copy</li>
              <li>Paste</li>
              <li>Delete</li>
            </ul>
          </div>
        )}
      </div>
    );
  }
}

export default App;

Preview

Dropdown menu {340x230}

Hope this article can help you.