简体   繁体   中英

Close react button dropdown menu on clicking or hovering outside of menu area

In my react application one of the component is creating a button dropdown menu like below.

<div class="dropdown">
    <button class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">Dropdown Example
    <span class="caret"></span></button>
    <ul class="dropdown-menu">
      <li><a onClick=doSomething href="#">HTML</a></li>
      <li><a onClick=doSomething href="#">CSS</a></li>
      <li><a onClick=doSomething href="#">JavaScript</a></li>
    </ul>   
</div>

How can I close this menu if someone clicks/hovers anywhere outside of this menu area?

Don't use jQuery events and DOM manipulations inside your React, you are missing the point.
You have a state use it!

In the following example you can see i removed all jquery.js and bootstrap.js files and kept only the bootstrap.css .
I use the state in order to show / hide the <ul> list (i've set it to display: block in order to override the default display: none of bootstrap.css ).
I have set 2 main handlers regarding showing and hiding the <ul> and 1 handler for the onclick of each item:

  1. toggleShow() - Toggle the show property in the state attached to the onBlur of the button.
  2. hide() - Hides the <ul> via the state of course (it does one more thing which i will explain).
  3. doSomething() - Will fire on click of an item.

This scenario has a nice challenge though, when we hide the <ul> we can't handle the click event.

But why?

Because we don't really hide it, it doesn't get rendered when state.show is false. So basically there's kind of a race condition of the setState the triggers a re-render against the handler of the doSomething event handler which can't run.
So the challenge here is to determine if active element that triggered the onBlur event of the button is the item inside the <ul> . If it is, then we need to let the item trigger it's onClick event and only then set the state and "hide" the .
We did that with the help of relatedTarget that attached to the event . This is the active element that we are looking for, and if we have one and it's not null then we trigger it's click event (no matter what element it is by the way as we don't care), and only then we set the state with show: false .

A working example:

 class DropDown extends React.Component{ constructor(props){ super(props); this.state = { show: false } this.doSomething = this.doSomething.bind(this); this.toggleShow = this.toggleShow.bind(this); this.hide = this.hide.bind(this); } doSomething(e){ e.preventDefault(); console.log(e.target.innerHTML); } toggleShow(){ this.setState({show: !this.state.show}); } hide(e){ if(e && e.relatedTarget){ e.relatedTarget.click(); } this.setState({show: false}); } render(){ return( <div className="dropdown"> <button className="btn btn-primary dropdown-toggle" type="button" onClick={this.toggleShow} onBlur={this.hide} > {"Dropdown Example"} <span className="caret"></span> </button> { this.state.show && ( <ul className="dropdown-menu" style={{display: 'block'}}> <li><a onClick={this.doSomething} href="#">HTML</a></li> <li><a onClick={this.doSomething} href="#">CSS</a></li> <li><a onClick={this.doSomething} href="#">JavaScript</a></li> </ul> ) } </div> ); } } ReactDOM.render(<DropDown />, document.getElementById('root')); 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script> <link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> <div id="root"></div> 

There are several ways to do this. The easiest way you can achieve this is by either having event listeners to listen to the click events or write an onBlur function for the dropdown component.

  componentWillMount() {
    document.body.addEventListener('click', this.handleClick);
  }

  componentWillUnmount() {
    document.body.removeEventListener('click', this.handleClick);
  }

And then you'll have to wrap your component inside the listener component.

Here is the working JS fiddle. https://jsfiddle.net/vamshikrishna144/zukcpfr2/

onBlur = {() => console.log("Clicked outside")}

The above method is triggered when clicked outside the Dropdown. You can change the states etc to show or hide your div or react dropdown

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM