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

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

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


当前回答

如果您需要typescript版本:

import React, { useRef, useEffect } from "react";

interface Props {
  ref: React.MutableRefObject<any>;

}

export const useOutsideAlerter = ({ ref }: Props) => {
  useEffect(() => {
    const handleClickOutside = (event: MouseEvent) => {
      if (ref.current && !ref.current.contains(event.target as Node)) {
       //do what ever you want
      }
    };
    // Bind the event listener
    document.addEventListener("mousedown", handleClickOutside);
    return () => {
      // Unbind the event listener on clean up
      document.removeEventListener("mousedown", handleClickOutside);
    };
  }, [ref]);
};
export default useOutsideAlerter;

如果您想扩展它以关闭模态或隐藏某些内容,也可以执行以下操作:

import React, { useRef, useEffect } from "react";

interface Props {
  ref: React.MutableRefObject<any>;
  setter: React.Dispatch<React.SetStateAction<boolean>>;
}

export const useOutsideAlerter = ({ ref, setter }: Props) => {
  useEffect(() => {
    const handleClickOutside = (event: MouseEvent) => {
      if (ref.current && !ref.current.contains(event.target as Node)) {
        setter(false);
      }
    };
    // Bind the event listener
    document.addEventListener("mousedown", handleClickOutside);
    return () => {
      // Unbind the event listener on clean up
      document.removeEventListener("mousedown", handleClickOutside);
    };
  }, [ref, setter]);
};
export default useOutsideAlerter;

其他回答

我知道这是一个老问题,但我不断遇到这个问题,我很难用简单的格式来解决这个问题。所以,如果这能让任何人的生活变得轻松一点,请使用airbnb的OutsideClickHandler。这是一个最简单的插件,无需编写自己的代码即可完成此任务。

例子:

hideresults(){
   this.setState({show:false})
}
render(){
 return(
 <div><div onClick={() => this.setState({show:true})}>SHOW</div> {(this.state.show)? <OutsideClickHandler onOutsideClick={() => 
  {this.hideresults()}} > <div className="insideclick"></div> </OutsideClickHandler> :null}</div>
 )
}

我从下面的文章中发现了这一点:

render(){返回({this.node=节点;}}>切换弹出窗口{this.state.popupVisible&&(我是个酒鬼!)});}}

这里有一篇关于这个问题的精彩文章:“处理React组件外部的点击”https://larsgraubner.com/handle-outside-clicks-react/

聚会晚了一点,但我在使用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
    }
}

如果您想使用这个功能已经存在的一个小组件(466字节gzip),那么您可以查看这个库react outclick。

该库的好处在于它还允许您检测组件外部和另一个组件内部的单击。它还支持检测其他类型的事件。

带钩的字体

注意:我使用的是React 16.3版,带有React.createRef。对于其他版本,使用ref回调。

下拉组件:

interface DropdownProps {
 ...
};

export const Dropdown: React.FC<DropdownProps> () {
  const ref: React.RefObject<HTMLDivElement> = React.createRef();
  
  const handleClickOutside = (event: MouseEvent) => {
    if (ref && ref !== null) {
      const cur = ref.current;
      if (cur && !cur.contains(event.target as Node)) {
        // close all dropdowns
      }
    }
  }

  useEffect(() => {
    // Bind the event listener
    document.addEventListener("mousedown", handleClickOutside);
    return () => {
      // Unbind the event listener on clean up
      document.removeEventListener("mousedown", handleClickOutside);
    };
  });

  return (
    <div ref={ref}>
        ...
    </div>
  );
}