简体   繁体   English

在菜单区域外单击或悬停时关闭反应按钮下拉菜单

[英]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.在我的 React 应用程序中,其中一个组件正在创建一个按钮下拉菜单,如下所示。

<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. 不要在你的React中使用jQuery事件和DOM操作,你错过了这一点。
You have a state use it! 你有一个state使用它!

In the following example you can see i removed all jquery.js and bootstrap.js files and kept only the bootstrap.css . 在下面的示例中,您可以看到我删除了所有jquery.jsbootstrap.js文件,并仅保留了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 ). 我使用state来显示/隐藏<ul>列表(我将其设置为display: block以覆盖默认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: 我已经设置了2个主要的处理程序,用于显示和隐藏每个项目的onclick<ul>和1个处理程序:

  1. toggleShow() - Toggle the show property in the state attached to the onBlur of the button. toggleShow() - 在附加到按钮的onBlurstate切换show属性。
  2. hide() - Hides the <ul> via the state of course (it does one more thing which i will explain). hide() - 通过状态隐藏<ul> (它还会做一件我要解释的事情)。
  3. doSomething() - Will fire on click of an item. doSomething() - 点击一个项目时会触发。

This scenario has a nice challenge though, when we hide the <ul> we can't handle the click event. 这种情况有一个很好的挑战,当我们隐藏<ul>我们无法处理click事件。

But why? 但为什么?

Because we don't really hide it, it doesn't get rendered when state.show is false. 因为我们并没有真正隐藏它,所以当state.show为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. 所以基本上有一种setState的竞争条件触发了对doSomething事件处理程序的处理程序的重新渲染,该处理程序无法运行。
So the challenge here is to determine if active element that triggered the onBlur event of the button is the item inside the <ul> . 因此,这里的挑战是确定触发按钮的onBlur事件的活动元素是否是<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 . 如果是,那么我们需要让项目触发它的onClick事件,然后才设置状态并“隐藏”。
We did that with the help of relatedTarget that attached to the event . 我们在附加到eventrelatedTarget的帮助下完成了这项工作。 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 . 这是我们正在寻找的活动元素,如果我们有一个并且它不是null那么我们触发它的click事件(不管它是什么元素,我们不关心的方式),然后我们才设置show: false状态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. 最简单的方法是让事件监听器监听click事件或为dropdown组件编写onBlur函数。

  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. 这是工作的JS小提琴。 https://jsfiddle.net/vamshikrishna144/zukcpfr2/ https://jsfiddle.net/vamshikrishna144/zukcpfr2/

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

The above method is triggered when clicked outside the Dropdown.在 Dropdown 外部单击时会触发上述方法。 You can change the states etc to show or hide your div or react dropdown您可以更改状态等以显示或隐藏您的 div 或反应下拉列表

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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