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

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

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


当前回答

对于那些需要绝对定位的人,我选择的一个简单选项是添加一个包装器组件,该组件的样式是以透明背景覆盖整个页面。然后可以在这个元素上添加一个onClick来关闭内部组件。

<div style={{
        position: 'fixed',
        top: '0', right: '0', bottom: '0', left: '0',
        zIndex: '1000',
      }} onClick={() => handleOutsideClick()} >
    <Content style={{position: 'absolute'}}/>
</div>

现在,如果您在内容上添加一个单击处理程序,那么事件也将传播到上面的div,从而触发handlerOutsideClick。如果这不是您想要的行为,只需停止处理程序上的事件进程。

<Content style={{position: 'absolute'}} onClick={e => {
                                          e.stopPropagation();
                                          desiredFunctionCall();
                                        }}/>

`

其他回答

我使用了这个模块(我与作者没有关联)

npm install react-onclickout --save

const ClickOutHandler=require('act-on-clickout');ExampleComponent类扩展React.Component{单击退出(e){if(hasClass(e.target,'忽略我'))返回;alert('用户在组件外部单击!');}render(){返回(<ClickOutHandler onClickOut={this.onClickOut}><div>点击我的外部</分区></ClickOutHandler>);}}

它做得很好。

我有一个类似的用例,我必须开发一个自定义下拉菜单。当用户在外面单击时,它应该自动关闭。以下是最近的React Hooks实现-

从“react”导入{useEffect,useRef,useState};导出常量应用程序=()=>{const-ref=useRef();const[isMenuOpen,setIsMenuOpen]=useState(false);使用效果(()=>{常量checkIfClickedOutside=(e)=>{//如果菜单是打开的并且点击的目标不在菜单内,//然后关闭菜单if(isMenuOpen&&ref.current&&!ref.current.contents(e.target)){setIsMenuOpen(false);}};document.addEventListener(“mousedown”,checkIfClickedOutside);返回()=>{//清理事件侦听器document.removeEventListener(“mousedown”,checkIfClickedOutside);};},[isMenuOpen]);返回(<div className=“wrapper”ref={ref}><按钮className=“button”onClick={()=>setIsMenuOpen((oldState)=>!oldState)}>单击我</按钮>{isMenuOpen&&(<ul className=“list”><li className=“list item”>下拉选项1</li><li className=“list item”>下拉选项2</li><li className=“list item”>下拉选项3</li><li className=“list item”>下拉选项4</li></ul>)}</div>);}

@ford04提案的字体+简化版:

使用OuterClick API

const Client = () => {
  const ref = useOuterClick<HTMLDivElement>(e => { /* Custom-event-handler */ });
  return <div ref={ref}> Inside </div> 
};

实施

export default function useOuterClick<T extends HTMLElement>(callback: Function) {
  const callbackRef = useRef<Function>(); // initialize mutable ref, which stores callback
  const innerRef = useRef<T>(null); // returned to client, who marks "border" element

  // update cb on each render, so second useEffect has access to current value
  useEffect(() => { callbackRef.current = callback; });

  useEffect(() => {
    document.addEventListener("click", _onClick);
    return () => document.removeEventListener("click", _onClick);
    function _onClick(e: any): void {
      const clickedOutside = !(innerRef.current?.contains(e.target));
      if (clickedOutside)
        callbackRef.current?.(e);
    }
  }, []); // no dependencies -> stable click listener

  return innerRef; // convenience for client (doesn't need to init ref himself)
}

聚会晚了一点,但我在使用React时遇到了一些问题。选择下拉菜单,因为单击的选项将不再包含在我希望在onClick被激发时单击的父项中。

我通过以下方式解决了这个问题:

componentDidMount() {
    document.addEventListener('mousedown', this.onClick );
}

componentWillUnmount() {
    document.removeEventListener('mousedown', this.onClick );
}

onClick = (event) => {
    if(!event.path.includes(this.detectOutsideClicksDiv)) {
        // Do stuff here
    }
}

以上答案中没有一个对我有效,所以我最终做了如下:

从“React”导入React,{Component};

/**
 * Component that alerts if you click outside of it
 */
export default class OutsideAlerter extends Component {
  constructor(props) {
    super(props);

    this.handleClickOutside = this.handleClickOutside.bind(this);
  }

  componentDidMount() {
    document.addEventListener('mousedown', this.handleClickOutside);
  }

  componentWillUnmount() {
    document.removeEventListener('mousedown', this.handleClickOutside);
  }

  /**
   * Alert if clicked on outside of element
   */
  handleClickOutside(event) {
    if (!event.path || !event.path.filter(item => item.className=='classOfAComponent').length) {
      alert('You clicked outside of me!');
    }
  }

  render() {
    return <div>{this.props.children}</div>;
  }
}

OutsideAlerter.propTypes = {
  children: PropTypes.element.isRequired,
};