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

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

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


当前回答

使用OnClickOutside Hook-反应16.8+

创建通用useOnOutsideClick函数

export const useOnOutsideClick = handleOutsideClick => {
  const innerBorderRef = useRef();

  const onClick = event => {
    if (
      innerBorderRef.current &&
      !innerBorderRef.current.contains(event.target)
    ) {
      handleOutsideClick();
    }
  };

  useMountEffect(() => {
    document.addEventListener("click", onClick, true);
    return () => {
      document.removeEventListener("click", onClick, true);
    };
  });

  return { innerBorderRef };
};

const useMountEffect = fun => useEffect(fun, []);

然后在任何功能组件中使用钩子。

const OutsideClickDemo = ({ currentMode, changeContactAppMode }) => {

  const [open, setOpen] = useState(false);
  const { innerBorderRef } = useOnOutsideClick(() => setOpen(false));

  return (
    <div>
      <button onClick={() => setOpen(true)}>open</button>
      {open && (
        <div ref={innerBorderRef}>
           <SomeChild/>
        </div>
      )}
    </div>
  );

};

链接到演示

部分灵感来自于@pau1itzgerald的回答。

其他回答

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

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

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

在这里尝试了许多方法之后,我决定使用github.com/Pomax/react-onclickoutside,因为它非常完整。

我通过npm安装了模块并将其导入到组件中:

import onClickOutside from 'react-onclickoutside'

然后,在组件类中,我定义了handleClickOutside方法:

handleClickOutside = () => {
  console.log('onClickOutside() method called')
}

导出组件时,我将其包装在onClickOutside()中:

export default onClickOutside(NameOfComponent)

就是这样。

或者:

const onClickOutsideListener = () => {
    alert("click outside")
    document.removeEventListener("click", onClickOutsideListener)
  }

...

return (
  <div
    onMouseLeave={() => {
          document.addEventListener("click", onClickOutsideListener)
        }}
  >
   ...
  </div>

Ez的方式。。。(2022年更新)

创建挂钩:useOutsideClick.ts

export function useOutsideClick(ref: any, onClickOut: () => void){
    useEffect(() => {
        const onClick = ({target}: any) => !ref.contains(target) && onClickOut?.()
        document.addEventListener("click", onClick);
        return () => document.removeEventListener("click", onClick);
    }, []);
}

将componentRef添加到组件并调用useOutsideClick

export function Example(){

  const componentRef = useRef();

  useOutsideClick(componentRef.current!, () => {
   // do something here
  });

  return ( 
    <div ref={componentRef as any}> My Component </div>
  )
}

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

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

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