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

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

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


当前回答

这是最适合我制作下拉菜单的方法:

handleClick = () => {
    document.getElementById("myDrop").classList.toggle("showing");
}

render() {

    return (
        <div className="courses">
            <div class="dropdownBody">
                <button onClick={this.handleClick} onBlur={this.handleClick} class="dropbtn">Dropdown</button>
                <div id="myDrop" class="dropdown-content">
                    <a href="#home">Home</a>
                    <a href="#about">About</a>
                    <a href="#contact">Contact</a>
                </div>
            </div>
        </div>
    )
}

其他回答

这是我的方法(演示-https://jsfiddle.net/agymay93/4/):

我创建了一个名为WatchClickOutside的特殊组件,它可以像这样使用(我假设JSX语法):

<WatchClickOutside onClickOutside={this.handleClose}>
  <SomeDropdownEtc>
</WatchClickOutside>

以下是WatchClickOutside组件的代码:

import React, { Component } from 'react';

export default class WatchClickOutside extends Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }

  componentWillMount() {
    document.body.addEventListener('click', this.handleClick);
  }

  componentWillUnmount() {
    // remember to remove all events to avoid memory leaks
    document.body.removeEventListener('click', this.handleClick);
  }

  handleClick(event) {
    const {container} = this.refs; // get container that we'll wait to be clicked outside
    const {onClickOutside} = this.props; // get click outside callback
    const {target} = event; // get direct click event target

    // if there is no proper callback - no point of checking
    if (typeof onClickOutside !== 'function') {
      return;
    }

    // if target is container - container was not clicked outside
    // if container contains clicked target - click was not outside of it
    if (target !== container && !container.contains(target)) {
      onClickOutside(event); // clicked outside - fire callback
    }
  }

  render() {
    return (
      <div ref="container">
        {this.props.children}
      </div>
    );
  }
}

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

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

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

您只需在主体上安装一个双击处理程序,并在此元素上安装另一个。在该元素的处理程序中,只需返回false以防止事件传播。因此,当双击发生时,如果它在元素上,它将被捕获,并且不会传播到主体上的处理程序。否则它会被身体上的处理程序抓住。

更新:如果你真的不想阻止事件传播,你只需要使用closest来检查点击是发生在你的元素还是他的一个孩子身上:

<html>
<head>
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<script>
$(document).on('click', function(event) {
    if (!$(event.target).closest('#div3').length) {
    alert("outside");
    }
});
</script>
</head>
<body>
    <div style="background-color:blue;width:100px;height:100px;" id="div1"></div>
    <div style="background-color:red;width:100px;height:100px;" id="div2"></div>
    <div style="background-color:green;width:100px;height:100px;" id="div3"></div>
    <div style="background-color:yellow;width:100px;height:100px;" id="div4"></div>
    <div style="background-color:grey;width:100px;height:100px;" id="div5"></div>
</body>
</html>

更新:不带jQuery:

<html>
<head>
<script>
function findClosest (element, fn) {
  if (!element) return undefined;
  return fn(element) ? element : findClosest(element.parentElement, fn);
}
document.addEventListener("click", function(event) {
    var target = findClosest(event.target, function(el) {
        return el.id == 'div3'
    });
    if (!target) {
        alert("outside");
    }
}, false);
</script>
</head>
<body>
    <div style="background-color:blue;width:100px;height:100px;" id="div1"></div>
    <div style="background-color:red;width:100px;height:100px;" id="div2"></div>
    <div style="background-color:green;width:100px;height:100px;" id="div3">
        <div style="background-color:pink;width:50px;height:50px;" id="div6"></div>
    </div>
    <div style="background-color:yellow;width:100px;height:100px;" id="div4"></div>
    <div style="background-color:grey;width:100px;height:100px;" id="div5"></div>
</body>
</html>

这里的其他答案都不适合我。我试图在模糊上隐藏一个弹出窗口,但由于内容是绝对定位的,所以即使单击内部内容,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.
    }
},

希望这有帮助。

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>
  )
}