我正在寻找一种方法来检测单击事件是否发生在组件之外,如本文所述。jQueryclosest()用于查看单击事件的目标是否将dom元素作为其父元素之一。如果存在匹配项,则单击事件属于其中一个子项,因此不被视为在组件之外。

因此,在我的组件中,我想将一个单击处理程序附加到窗口。当处理程序启动时,我需要将目标与组件的dom子级进行比较。

click事件包含类似“path”的财产,它似乎保存了事件经过的dom路径。我不知道该比较什么,或者如何最好地遍历它,我想肯定有人已经把它放在了一个聪明的效用函数中。。。不


当前回答

我对所有其他答案最担心的是必须从根/父级向下过滤单击事件。我发现最简单的方法是简单地设置一个具有位置的同级元素:fixed,下拉列表后面的z索引1,并处理同一组件内固定元素上的单击事件。将所有内容集中到给定组件。

示例代码

#HTML
<div className="parent">
  <div className={`dropdown ${this.state.open ? open : ''}`}>
    ...content
  </div>
  <div className="outer-handler" onClick={() => this.setState({open: false})}>
  </div>
</div>

#SASS
.dropdown {
  display: none;
  position: absolute;
  top: 0px;
  left: 0px;
  z-index: 100;
  &.open {
    display: block;
  }
}
.outer-handler {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    opacity: 0;
    z-index: 99;
    display: none;
    &.open {
      display: block;
    }
}

其他回答

这里的其他答案都不适合我。我试图在模糊上隐藏一个弹出窗口,但由于内容是绝对定位的,所以即使单击内部内容,onBlur也会启动。

以下是一个对我来说行之有效的方法:

// Inside the component:
onBlur(event) {
    // currentTarget refers to this component.
    // relatedTarget refers to the element where the user clicked (or focused) which
    // triggered this event.
    // So in effect, this condition checks if the user clicked outside the component.
    if (!event.currentTarget.contains(event.relatedTarget)) {
        // do your thing.
    }
},

希望这有帮助。

我对所有其他答案最担心的是必须从根/父级向下过滤单击事件。我发现最简单的方法是简单地设置一个具有位置的同级元素:fixed,下拉列表后面的z索引1,并处理同一组件内固定元素上的单击事件。将所有内容集中到给定组件。

示例代码

#HTML
<div className="parent">
  <div className={`dropdown ${this.state.open ? open : ''}`}>
    ...content
  </div>
  <div className="outer-handler" onClick={() => this.setState({open: false})}>
  </div>
</div>

#SASS
.dropdown {
  display: none;
  position: absolute;
  top: 0px;
  left: 0px;
  z-index: 100;
  &.open {
    display: block;
  }
}
.outer-handler {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    opacity: 0;
    z-index: 99;
    display: none;
    &.open {
      display: block;
    }
}

我在discuss.reactjs.org上找到了一个解决方案,这要感谢Ben Alpert。单击我的树中的一个组件会导致一个重新阅读程序,在更新时删除了单击的元素。由于React的重读发生在调用文档正文处理程序之前,因此元素未被检测为“在”树中。

解决方案是在应用程序根元素上添加处理程序。

主要:

window.__myapp_container = document.getElementById('app')
React.render(<App/>, window.__myapp_container)

组件:

import { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';

export default class ClickListener extends Component {

  static propTypes = {
    children: PropTypes.node.isRequired,
    onClickOutside: PropTypes.func.isRequired
  }

  componentDidMount () {
    window.__myapp_container.addEventListener('click', this.handleDocumentClick)
  }

  componentWillUnmount () {
    window.__myapp_container.removeEventListener('click', this.handleDocumentClick)
  }

  /* using fat arrow to bind to instance */
  handleDocumentClick = (evt) => {
    const area = ReactDOM.findDOMNode(this.refs.area);

    if (!area.contains(evt.target)) {
      this.props.onClickOutside(evt)
    }
  }

  render () {
    return (
      <div ref='area'>
       {this.props.children}
      </div>
    )
  }
}

除了.concurs之外,还可以使用最接近的方法。当您想检查单击是否在id=“apple”的元素之外时,我可以使用:

const isOutside = !e.target.closest("#apple");

这将检查单击的树上方的树中是否有任何元素的id为“apple”。我们必须否定结果!

只需使用mui(material ui)中的ClickAwayListener:

<ClickAwayListener onClickAway={handleClickAway}>
    {children}
<ClickAwayListener >

有关更多信息,请查看:https://mui.com/base/react-click-away-listener/