首页 > 解决方案 > 反应:下拉菜单没有在外面的水龙头上关闭

问题描述

我有以下代码用于为某些项目创建下拉列表:

import React, { Component } from "react"
import { connectRefinementList } from "react-instantsearch-dom"
import PropTypes from "prop-types"

const cx = label => `ais-DropdownRefinementList-${label}`

class DropdownRefinementList extends Component {
  constructor(props) {
    super(props)

    this.state = {
      active: false,
      mobile: false,
    }
  }

  componentDidMount() {
    this.setState({
      mobile: /Mobi/.test(navigator.userAgent),
    })
  }
  capitalizeFirst(s) {
    return s.charAt(0).toUpperCase() + s.slice(1)
  }
  renderItem = (item, i) => (
    <label key={i} className="ais-DropdownRefinementList-item-label">
      <input
        type="checkbox"
        checked={item.isRefined}
        onChange={() => {
          this.selectItem(item)
        }}
      />
      <span>{item.label}</span>
      <span className={cx("item-count")}> ({item.count})</span>
    </label>
  )
  selectItem = (item, resetQuery) => {
    this.props.refine(item.value)
  }
  handleEvent = e => {
    this.setState({ active: !this.state.active })
  }
  render() {
    const { items, attribute, hoverable, currentRefinement, icon } = this.props
    const { active, mobile } = this.state
    const title = this.capitalizeFirst(attribute)
    console.log(this.state)
    return (
      <div
        className={`ais-DropdownRefinementList-container-${attribute}`}
        onMouseLeave={hoverable && !mobile && this.handleEvent}
        onMouseEnter={hoverable && !mobile && this.handleEvent}
      >
        <div className={cx("title-container")} onClick={this.handleEvent}>
          <span class="w-full focus:text-teal-500 hover:text-teal-500 justify-center inline-block text-gray-600 text-xl text-center ">
            {icon}
          </span>
          <span className="ais-DropdownRefinementList-title font-bold w-full text-gray-600">
            {title}{" "}
          </span>
        </div>
        {active && (
          <div className={`ais-DropdownRefinementList-List ${attribute}`}>
            {items.map(this.renderItem)}
          </div>
        )}
      </div>
    )
  }
}

DropdownRefinementList.propTypes = {
  attribute: PropTypes.string.isRequired,
  icon: PropTypes.string.isRequired,
  hoverable: PropTypes.bool,
  limit: PropTypes.number,
}
DropdownRefinementList.defaultProps = {
  hoverable: false,
}

export default connectRefinementList(DropdownRefinementList)

在笔记本电脑上,这些工作正常,当我将鼠标悬停在列表标题上时,下拉菜单打开,当我离开时,下拉菜单关闭。我曾认为当我在移动设备上时也会发生同样的情况。然而,这种情况并非如此。当用户单击另一个下拉菜单时,一个下拉菜单将保持打开状态。如下所示:

在此处输入图像描述

尽管有onMouseLeave和的条件onMouseEnter。这不适用于移动设备吗?容器外的水龙头不应该再次隐藏它吗,就像它应该的那样?

我怎样才能使这项工作也适用于手机?

如果有帮助,这是该组件的 CSS,这一个实时部署(您必须使用 Inspect Element 切换到移动视图,因为它隐藏在大屏幕上)。

标签: javascripthtmlcssreactjs

解决方案


我查看了一些有关如何在移动设备上翻译鼠标事件的文档:https ://developer.mozilla.org/en-US/docs/Web/API/Touch_events/Supporting_both_TouchEvent_and_MouseEvent

我的建议是不要在移动设备上依赖 mouseenter 和 mouseleave。我会激活一个道具,让父母跟踪哪些 DropdownRefinementLists 应该是可见的。那是因为 DropdownRefinementList 看不到外面的点击事件。我将处理接收外部空间事件的祖先中的“外部点击”事件。

示例代码:

这是(新)DropdownRefinementListthis.props.active是真还是假,取决于它是否应该显示弹出窗口。this.props.setActive()它在收到点击/触摸时调用。

class DropdownRefinementList extends React.Component {
  constructor(props) {
    super(props)
  }

  handleEvent = e => {
    this.props.setActive();
    e.stopPropagation(); //stop event from propagating to ancestor elements
  }
  render() {
    
    return (
      <div onClick={this.handleEvent} style={{position: 'relative', padding: '5px'}}>
        <h3>
            {this.props.title}
        </h3>
        <div style={{marginTop: '1rem'}}>
          Click me!
        </div>
        {this.props.active && (
          <div style={{
                position: 'absolute',
                top: '-50px', left: '25px',
                height: '150px', width: '200px',
                borderStyle: 'solid',
                backgroundColor: '#ccc',
                padding: '5px',
                zIndex: 100
                }}>
            {this.props.title}{' '} Popup
          </div>
        )}
      </div>
    )
  }
}

这是父组件。this.state.activeDropdown采用以下 3 个值:

  • 0:没有下拉菜单处于活动状态
  • 1:下拉菜单 1 处于活动状态
  • 2:下拉菜单 2 处于活动状态

this.state.activeDropdown当它收到点击/触摸时,它设置为 0。

class DropdownRefinementListParent extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
        activeDropdown: 0
    }
  }
  
  setActive = (whichDropdown) => {
    this.setState({activeDropdown: whichDropdown});
  }
  handleEvent = (e, whichDropdown) => {
    
    this.setActive(0);
    e.stopPropagation(); //stop event from propagating to ancestor elements
  }  
  render() {
    const dropdown1IsActive = (this.state.activeDropdown === 1);
    const dropdown2IsActive = (this.state.activeDropdown === 2);
  
    return (
        <div onClick={e => this.handleEvent(e, 0)}>
      
        <h2>DropdownRefinementList Parent</h2>
      
        <div>activeDropdown: {this.state.activeDropdown}</div>
      
        <div style={{display: 'flex', margin: '10px 0', width: '600px', justifyContent: 'flex-start'}}>
        
          <div style={{flexGrow: 0, borderStyle: 'solid', margin: '5px'}}>
            <DropdownRefinementList
                title="DropdownRefinementList 1"
                active={dropdown1IsActive}
                setActive={() => this.setActive(1)} />
          </div>
          
          <div style={{flexGrow: 0, borderStyle: 'solid', margin: '5px'}}>
            <DropdownRefinementList
                title="DropdownRefinementList 2"
                active={dropdown2IsActive}
                setActive={() => this.setActive(2)} />
          </div>
        </div>
        end
      </div>
    )
  }
}

jsFiddle 上的现场演示


推荐阅读