【问题标题】:click anywhere to close dropdown in react单击任意位置以关闭反应中的下拉列表
【发布时间】:2017-11-20 01:15:24
【问题描述】:

我在一个页面上有多个自定义构建下拉组件。我使用 setState 触发列表项打开

toggleDropdown = (id) => {
        this.setState(prevState => ({
            [`dropdown${name}`]: !prevState[`dropdown${id}`] //dropdownA, dropdownB, dropdownC and so on
        }))
    }

如果在菜单打开时单击下拉菜单,这也会切换它。但是后来我有几个下拉菜单,如果我打开一个下拉菜单,其他下拉菜单不会关闭,如何解决这个问题?我在componentWillMount中用jquery混合反应,在body上绑定click事件,检查下拉项是否可见,如果是则关闭它。

我的问题是,有没有更好的做法来避免使用 jquery?

【问题讨论】:

  • 你能提供一个你的 app.js 的例子吗?

标签: javascript jquery reactjs ecmascript-6


【解决方案1】:

我的建议是您使用合成事件onFocusonBlur 来触发打开/关闭状态。 onFocus 将在单击元素时触发,onBlur 将在“不聚焦”(单击外部)时触发。见docs

还需要tabIndex 属性/prop 才能使焦点/模糊作用于非输入类型元素。

我可以推荐查看source of react-select 以及它们如何处理聚焦/模糊。

这是一个例子,你可以看到它的演示here

import React from "react";

class Dropdown extends React.Component {
  state = {
    open: false
  };

  toggleDropdown() {
    this.setState({ open: !this.state.open });
  }

  render() {
    return (
      <div
        style={{ border: "1px solid #CCC" }}
        onBlur={() => this.toggleDropdown()}
        onFocus={() => this.toggleDropdown()}
        tabIndex="0"
      >
        Dropdown
        {this.state.open && (
          <div>
            <div>Red</div>
            <div>Green</div>
            <div>Blue</div>
          </div>
        )}
      </div>
    );
  }
}

export default Dropdown;

【讨论】:

  • @heyhugo 你知道如何让子元素 (&lt;div&gt;Red&lt;/div&gt;) 在不最小化父元素的情况下也可以聚焦吗?
  • @JohnCullen 使用 node.contains() (browser DOM) API 检查父元素是否包含被点击的元素。你可以在这里看到它是如何完成的:github.com/JedWatson/react-select/blob/master/src/…
  • 谢谢,我正在尝试模仿 CSS 伪标签 focus-within,并且我做到了合理的程度。我无法真正处理的唯一方面是从最后一个元素中抽出最小化下拉菜单。不过感谢您的帮助!
  • 好吧,不知道你有什么选项卡问题,但也许这个资源有帮助:developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/… tabindex 属性 = tabIndex prop in react
  • 如果按钮不在下拉菜单中,我将如何实现这个?
【解决方案2】:

您可以使用react-onclickoutside 库为您抽象出正文事件处理。您只需将您的组件包装在其onClickOutside 高阶组件中,该组件将在单击发生在下拉列表之外的任何时候执行handleClickOutside 回调。这样您就可以在所有下拉菜单中拥有一个私有的“打开”状态,而不必关心处理是如何实现的。

代码可能如下所示:

import React, { Component } from 'react'
import onClickOutside from 'react-onclickoutside'

class Dropdown extends Component {
    constructor() {
        this.state = {open: false}
    }

    // Method automatically executed by the library.
    handleClickOutside() {
        this.setState({open: false})
    }

    render() { ... }
}

export default onClickOutside(Dropdown)

【讨论】:

    【解决方案3】:

    首先,我们创建打开值为 false 的状态,因此下拉菜单默认关闭。然后,我们将为单击添加一个事件侦听器,该事件侦听器将关闭下拉菜单并在下拉菜单关闭后移除事件侦听器。然后,我们向按钮添加一个 onClick 事件,以使用 toggleOpen() 将状态从 false 更改为 true。然后我们将我们的内容包装在 if/or 中以检查内容是否打开:

    `class Dropdown extends React.Component {
    
         constructor() {
             super();
    
             this.state = {
                 open: false
             }
             this.toggleOpen = this.toggleOpen.bind(this);
             this.toggleClosed = this.toggleClosed.bind(this);
         }
    
        toggleOpen() {
            this.setState({ open: true }, () => {
                document.addEventListener('click', this.toggleClosed)
            })
        }
    
       toggleClosed() {
           this.setState({ open: false}, () => {
               document.removeEventListener('click', this.toggleClosed)
           })
       }
    
       render() {
           return(
               <div>
                   <button onClick=({ this.toggleOpen })>
                       Dropdown
                   </button>
                   {
                        this.state.open
                        ? (
                              <span> Dropdown</span><br /><br />
                              <span> content</span><br /><br />
                          ) :
                          (
                              null
                          )
            </div>
       }
       }`
    

    【讨论】:

      猜你喜欢
      • 2021-01-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多