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

IT技术 javascript html css reactjs
2021-05-14 09:40:21

在我的 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>

如果有人单击/悬停在此菜单区域之外的任何位置,我该如何关闭此菜单?

2个回答

不要在你的 React 中使用 jQuery 事件和 DOM 操作,你没有抓住重点。
你有一个state使用它!

在以下示例中,您可以看到我删除了所有jquery.jsbootstrap.js文件并仅保留了bootstrap.css.
我用的是state为了显示/隐藏<ul>列表(我已经将其设置为display: block以覆盖默认display: nonebootstrap.css)。
我设置了 2 个主要处理程序,用于显示和隐藏每个项目<ul>和 1 个处理程序onclick

  1. toggleShow()- 切换附加到按钮show属性stateonBlur
  2. hide()-<ul>当然隐藏via 状态(它还有一件事我将解释)。
  3. doSomething() - 将在点击项目时触发。

这个场景有一个很好的挑战,当我们隐藏时,<ul>我们无法处理点击事件。

但为什么?

因为我们并没有真正隐藏它,所以它在state.show为 false时不会被渲染所以基本上有一种竞争条件setState触发重新渲染对doSomething无法运行事件处理程序的处理程序。
所以这里的挑战是确定触发onBlur按钮事件的活动元素是否是<ul>. 如果是,那么我们需要让项目触发它的onClick事件,然后才设置状态并“隐藏” .
我们在relatedTarget附加到event. 这是我们正在寻找的活动元素,如果我们有一个而没有,null那么我们触发它的点击事件(顺便说一下,不管它是什么元素,因为我们不在乎),show: false.

一个工作示例:

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>

有几种方法可以做到这一点。实现此目的的最简单方法是使用事件侦听器来侦听单击事件或为下拉组件编写 onBlur 函数。

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

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

然后您必须将您的组件包装在侦听器组件中。

这是工作中的 JS 小提琴。https://jsfiddle.net/vamshikrishna144/zukcpfr2/