2

I have a React js App (No JQuery please) drop down menu that I trigger when the ▼ character is clicked. With my code below, the dropdown menu disappears only when the same ▼ is clicked again. I want to make it disappear when the user clicks anywhere else on the page. How to change the code below to make the dropdown menu disappear when the user clicks anywhere else on the page as well and not just by clicking the same icon ▼ ?

Toggler Icon:

      <span className="show_more" onClick={this.toggleOptions}>
        <MaterialIcon icon="keyboard_arrow_down" />
      </span>

Code for toggling: (used by many components so can the fix be only here ?)

import React, { Component } from 'react'
...
import MaterialIcon from '../icons/material-icon'
import HeaderLogo from './logo'

export default class Header extends Component {
  state = {
    showOptions: false,
  }

  toggleOptions = () => this.setState({ showOptions: !this.state.showOptions })

  render() {
    let { showOptions } = this.state

    return (
      <div className="header">
        <div className="row-container container">
          <div className="col-l">
            <HeaderLogo />
          </div>
          <div className="col-m">
            <Search />
          </div>
          <div className="col-r">
            <div className="header_right">
              <HeaderTopLinks />
              <span className="show_more" onClick={this.toggleOptions}>
                <MaterialIcon icon="keyboard_arrow_down" />
              </span>
            </div>

            {showOptions ? (
              <HeaderOptions toggleOptions={this.toggleOptions} />
            ) : null}
          </div>
        </div>
      </div>
    )
  }
}
Kal
  • 1,656
  • 4
  • 26
  • 41

1 Answers1

3

The answer can be found here

But to sum it up, you have to listen for clicks on the document, and write a function that will walk the tree and tell you if the click occurred inside or outside your component

Here are the important bits from that link to add to your component:

  handleClickOutside(event) {
    if (this.wrapperRef && !this.wrapperRef.contains(event.target)) {
      alert('You clicked outside of me!');
      this.setState({ showOptions: false });
    }
  }

  componentDidMount() {
    document.addEventListener('mousedown', this.handleClickOutside);
  }

  componentWillUnmount() {
    document.removeEventListener('mousedown', this.handleClickOutside);
  }

  render() {
    let { showOptions } = this.state;

    return <div className="header" ref={(node) => this.setWrapperRef = node}>...all the rest of the component goes here...</div>;
  }

For the record, there are many ways of accomplishing this, this is just one approach.

James
  • 4,927
  • 3
  • 22
  • 27
  • Thank you but I think your answer is very generic. I posted my component code to help with answers - and looking to see how I can accomplish this with modification to just the toggle component and not all components that call the toggler. – Kal Sep 07 '18 at 22:59
  • I have no idea what you mean. This code does not modify any components that call the toggler. It just determines if a user clicks outside of the dropdown, which is what you asked for help with. – James Sep 07 '18 at 23:43
  • 'Code for toggling (above)' - Thats the code for toggling the dropdown. I am asking of the fix can be in that component. If not I will have to modify many components that call this code. I am not saying your answer will not work but I think that does not use the code I posted. Its a generic answer. – Kal Sep 08 '18 at 00:04
  • No dude, you add the code I provided to your component and it will fix your problem. You don't have to add it to any other components. – James Sep 08 '18 at 00:49
  • Ok, I will try it out. Where does this code go above as I have lost of stuff in my return ? `return
    this.setWrapperRef = node}>dropdown stuff
    ;`
    – Kal Sep 08 '18 at 02:10
  • You put that `ref` on the outermost element, I've updated my answer to try and describe the solution without copying your entire question – James Sep 08 '18 at 02:33
  • I was confused when you added `dropdown stuff here`... I think it should go around ` – Kal Sep 08 '18 at 02:39
  • A few things were missing such as constructor code `setWrapperRef(node)` etc. But in the end it works. Thank you – Kal Sep 08 '18 at 02:58