检测React组件外部的点击

2020/09/27 03:41 · javascript ·  · 0评论

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

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

click事件包含“ path”之类的属性,该属性似乎包含事件经过的dom路径。我不确定要比较什么或如何最好地遍历它,并且我认为有人必须已经将其放在聪明的实用程序函数中了……不?

React 16.3+中的引用用法已更改。

以下解决方案使用ES6,并遵循最佳实践进行绑定以及通过一种方法设置ref。

要查看实际效果:

类的实现:

import React, { Component } from 'react';
import PropTypes from 'prop-types';

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

        this.wrapperRef = React.createRef();
        this.setWrapperRef = this.setWrapperRef.bind(this);
        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 (this.wrapperRef && !this.wrapperRef.current.contains(event.target)) {
            alert('You clicked outside of me!');
        }
    }

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

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

挂钩实现:

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

/**
 * Hook that alerts clicks outside of the passed ref
 */
function useOutsideAlerter(ref) {
    useEffect(() => {
        /**
         * Alert if clicked on outside of element
         */
        function handleClickOutside(event) {
            if (ref.current && !ref.current.contains(event.target)) {
                alert("You clicked outside of me!");
            }
        }

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

/**
 * Component that alerts if you click outside of it
 */
export default function OutsideAlerter(props) {
    const wrapperRef = useRef(null);
    useOutsideAlerter(wrapperRef);

    return <div ref={wrapperRef}>{props.children}</div>;
}

这是不将事件附加到容器的最适合我的解决方案:

某些HTML元素可以具有所谓的“ 焦点 ”,例如输入元素。当这些元素失去焦点时,它们也会响应模糊事件。

要使任何元素都有焦点的能力,只需确保其tabindex属性设置为-1以外的任何值即可。在常规HTML中,可以通过设置tabindex属性来实现,但是在React中,必须使用tabIndex(请注意大写I)。

您也可以通过JavaScript使用 element.setAttribute('tabindex',0)

这就是我用来制作自定义DropDown菜单的目的。

var DropDownMenu = React.createClass({
    getInitialState: function(){
        return {
            expanded: false
        }
    },
    expand: function(){
        this.setState({expanded: true});
    },
    collapse: function(){
        this.setState({expanded: false});
    },
    render: function(){
        if(this.state.expanded){
            var dropdown = ...; //the dropdown content
        } else {
            var dropdown = undefined;
        }

        return (
            <div className="dropDownMenu" tabIndex="0" onBlur={ this.collapse } >
                <div className="currentValue" onClick={this.expand}>
                    {this.props.displayValue}
                </div>
                {dropdown}
            </div>
        );
    }
});

我陷入了同一个问题。我在这里参加聚会有点晚了,但是对我来说这是一个非常好的解决方案。希望它将对其他人有所帮助。您需要进口findDOMNodereact-dom

import ReactDOM from 'react-dom';
// ... ✂

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

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

handleClickOutside = event => {
    const domNode = ReactDOM.findDOMNode(this);

    if (!domNode || !domNode.contains(event.target)) {
        this.setState({
            visible: false
        });
    }
}

React Hooks方法(16.8 +)

您可以创建一个名为的可重用挂钩useComponentVisible

import { useState, useEffect, useRef } from 'react';

export default function useComponentVisible(initialIsVisible) {
    const [isComponentVisible, setIsComponentVisible] = useState(initialIsVisible);
    const ref = useRef(null);

    const handleClickOutside = (event) => {
        if (ref.current && !ref.current.contains(event.target)) {
            setIsComponentVisible(false);
        }
    };

    useEffect(() => {
        document.addEventListener('click', handleClickOutside, true);
        return () => {
            document.removeEventListener('click', handleClickOutside, true);
        };
    });

    return { ref, isComponentVisible, setIsComponentVisible };
}

然后,您希望在组件中添加功能以执行以下操作:

const DropDown = () => {
    const { ref, isComponentVisible } = useComponentVisible(true);
    return (
       <div ref={ref}>
          {isComponentVisible && (<p>Dropdown Component</p>)}
       </div>
    );

}

在此处找到一个codeandbox示例。

在尝试了许多方法之后,由于它的完整性,我决定使用github.com/Pomax/react-onclickoutside

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

import onClickOutside from 'react-onclickoutside'

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

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

在导出组件时,我将其包裹在onClickOutside()

export default onClickOutside(NameOfComponent)

而已。

基于Tanner Linsley 在JSConf Hawaii 2020上的精彩演讲的 Hook实现

useOuterClick 挂钩API

const Client = () => {
  const innerRef = useOuterClick(ev => { /* what you want do on outer click */ });
  return <div ref={innerRef}> Inside </div> 
};

实作

/*
  Custom Hook
*/
function useOuterClick(callback) {
  const innerRef = useRef();
  const callbackRef = useRef();

  // set current callback in ref, before second useEffect uses it
  useEffect(() => { // useEffect wrapper to be safe for concurrent mode
    callbackRef.current = callback;
  });

  useEffect(() => {
    document.addEventListener("click", handleClick);
    return () => document.removeEventListener("click", handleClick);

    // read most recent callback and innerRef dom node from refs
    function handleClick(e) {
      if (
        innerRef.current && 
        callbackRef.current &&
        !innerRef.current.contains(e.target)
      ) {
        callbackRef.current(e);
      }
    }
  }, []); // no need for callback + innerRef dep
  
  return innerRef; // return ref; client can omit `useRef`
}

/*
  Usage 
*/
const Client = () => {
  const [counter, setCounter] = useState(0);
  const innerRef = useOuterClick(e => {
    // counter state is up-to-date, when handler is called
    alert(`Clicked outside! Increment counter to ${counter + 1}`);
    setCounter(c => c + 1);
  });
  return (
    <div>
      <p>Click outside!</p>
      <div id="container" ref={innerRef}>
        Inside, counter: {counter}
      </div>
    </div>
  );
};

ReactDOM.render(<Client />, document.getElementById("root"));
#container { border: 1px solid red; padding: 20px; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js" integrity="sha256-Ef0vObdWpkMAnxp39TYSLVS/vVUokDE8CDFnx7tjY6U=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.12.0/umd/react-dom.production.min.js" integrity="sha256-p2yuFdE8hNZsQ31Qk+s8N+Me2fL5cc6NKXOC0U9uGww=" crossorigin="anonymous"></script>
<script> var {useRef, useEffect, useCallback, useState} = React</script>
<div id="root"></div>

useOuterClick利用可变引用为。创建精益API Client可以设置回调,而无需通过来记住useCallback回调主体仍然可以访问最新的道具和状态(没有过期的关闭值)。


iOS用户注意事项

iOS仅将某些元素视为可点击的。要避免这种情况,请选择其他外部点击侦听器,而不要选择document-包括在内的所有内容body例如,您可以div在上面的示例中在React根上添加一个侦听器,并扩展其高度(height: 100vh或类似高度)以捕获所有外部点击。资料来源:quirksmode.org这个答案

感谢Ben Alpert在describe.reactjs.org上找到了一个解决方案建议的方法将处理程序附加到文档,但是事实证明是有问题的。单击我的树中的一个组件会导致重新渲染,从而删除了更新时单击的元素。由于从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>
    )
  }
}

这里没有其他答案对我有用。我试图隐藏一个模糊的弹出窗口,但是由于内容是绝对定位的,因此即使单击内部内容,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.
    }
},

希望这可以帮助。

[更新]使用Hooks的React ^ 16.8解决方案

CodeSandbox

import React, { useEffect, useRef, useState } from 'react';

const SampleComponent = () => {
    const [clickedOutside, setClickedOutside] = useState(false);
    const myRef = useRef();

    const handleClickOutside = e => {
        if (!myRef.current.contains(e.target)) {
            setClickedOutside(true);
        }
    };

    const handleClickInside = () => setClickedOutside(false);

    useEffect(() => {
        document.addEventListener('mousedown', handleClickOutside);
        return () => document.removeEventListener('mousedown', handleClickOutside);
    });

    return (
        <button ref={myRef} onClick={handleClickInside}>
            {clickedOutside ? 'Bye!' : 'Hello!'}
        </button>
    );
};

export default SampleComponent;

使用React ^ 16.3的解决方案

CodeSandbox

import React, { Component } from "react";

class SampleComponent extends Component {
  state = {
    clickedOutside: false
  };

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

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

  myRef = React.createRef();

  handleClickOutside = e => {
    if (!this.myRef.current.contains(e.target)) {
      this.setState({ clickedOutside: true });
    }
  };

  handleClickInside = () => this.setState({ clickedOutside: false });

  render() {
    return (
      <button ref={this.myRef} onClick={this.handleClickInside}>
        {this.state.clickedOutside ? "Bye!" : "Hello!"}
      </button>
    );
  }
}

export default SampleComponent;

这是我的方法(演示-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>
    );
  }
}

这已经有很多答案,但是它们没有解决,e.stopPropagation()并且无法单击您要关闭的元素之外的react链接。

由于React具有自己的人工事件处理程序,因此您无法将文档用作事件侦听器的基础。e.stopPropagation()在此之前,您需要做为React使用文档本身。如果使用示例document.querySelector('body')代替。您可以阻止来自React链接的点击。以下是我如何实现外部点击和关闭的示例。

这使用了
ES6React 16.3

import React, { Component } from 'react';

class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      isOpen: false,
    };

    this.insideContainer = React.createRef();
  }

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

  componentWillUnmount() {
    document.querySelector('body').removeEventListener("click", this.handleClick, false);
  }

  handleClick(e) {
    /* Check that we've clicked outside of the container and that it is open */
    if (!this.insideContainer.current.contains(e.target) && this.state.isOpen === true) {
      e.preventDefault();
      e.stopPropagation();
      this.setState({
        isOpen: false,
      })
    }
  };

  togggleOpenHandler(e) {
    e.preventDefault();

    this.setState({
      isOpen: !this.state.isOpen,
    })
  }

  render(){
    return(
      <div>
        <span ref={this.insideContainer}>
          <a href="#open-container" onClick={(e) => this.togggleOpenHandler(e)}>Open me</a>
        </span>
        <a href="/" onClick({/* clickHandler */})>
          Will not trigger a click when inside is open.
        </a>
      </div>
    );
  }
}

export default App;

Material-UI有一个解决此问题的小组件:https : //material-ui.com/components/click-away-listener/,您可以对其进行挑选。压缩后的重量为1.5 kB,它支持移动设备,IE 11和门户。

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

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

现在,如果您在内容上添加单击处理程序,则该事件还将传播到上层div,因此触发该处理程序OutsideClick。如果这不是您想要的行为,只需在处理程序上停止事件传播。

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

`

要扩展Ben Bud所接受的答案,如果您使用的是样式组件,则以这种方式传递ref会给您带来错误,例如“ this.wrapperRef.contains不是函数”。

建议的修复方法(在注释中)将带div的样式化组件包装并在其中传递ref起作用。话虽如此,他们已经在他们的文档中解释了这样做的原因以及在styled-components中正确使用ref的原因:

将ref prop传递给样式化组件将为您提供StyledComponent包装器的实例,但不会传递给基础DOM节点。这是由于ref的工作方式。无法直接在我们的包装器上调用DOM方法(如focus)。要获得对实际包装的DOM节点的引用,请将该回调传递给innerRef属性。

像这样:

<StyledDiv innerRef={el => { this.el = el }} />

然后,您可以在“ handleClickOutside”函数中直接访问它:

handleClickOutside = e => {
    if (this.el && !this.el.contains(e.target)) {
        console.log('clicked outside')
    }
}

这也适用于“ onBlur”方法:

componentDidMount(){
    this.el.focus()
}
blurHandler = () => {
    console.log('clicked outside')
}
render(){
    return(
        <StyledDiv
            onBlur={this.blurHandler}
            tabIndex="0"
            innerRef={el => { this.el = el }}
        />
    )
}

我这样做的部分原因是遵循此要求,并遵循React官方文档中关于处理需要react ^ 16.3的refs的文档。这是我在这里尝试了其他一些建议后唯一对我有用的东西...

class App extends Component {
  constructor(props) {
    super(props);
    this.inputRef = React.createRef();
  }
  componentWillMount() {
    document.addEventListener("mousedown", this.handleClick, false);
  }
  componentWillUnmount() {
    document.removeEventListener("mousedown", this.handleClick, false);
  }
  handleClick = e => {
    /*Validating click is made inside a component*/
    if ( this.inputRef.current === e.target ) {
      return;
    }
    this.handleclickOutside();
  };
  handleClickOutside(){
    /*code to handle what to do when clicked outside*/
  }
  render(){
    return(
      <div>
        <span ref={this.inputRef} />
      </div>
    )
  }
}

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

范例程式码

#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;
    }
}
componentWillMount(){

  document.addEventListener('mousedown', this.handleClickOutside)
}

handleClickOutside(event) {

  if(event.path[0].id !== 'your-button'){
     this.setState({showWhatever: false})
  }
}

活动path[0]是最后点击的项目

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

npm install react-onclickout --save

const ClickOutHandler = require('react-onclickout');
 
class ExampleComponent extends React.Component {
 
  onClickOut(e) {
    if (hasClass(e.target, 'ignore-me')) return;
    alert('user clicked outside of the component!');
  }
 
  render() {
    return (
      <ClickOutHandler onClickOut={this.onClickOut}>
        <div>Click outside of me!</div>
      </ClickOutHandler>
    );
  }
}

它做得很好。

import { useClickAway } from "react-use";

useClickAway(ref, () => console.log('OUTSIDE CLICKED'));

策略的一个例子

我喜欢提供的解决方案,这些解决方案通过在组件周围创建包装器来执行相同的操作。

由于这更多是一种行为,因此我想到了Strategy并提出了以下建议。

我是React的新手,我需要一点帮助,以便在用例中节省一些样板

请查看并告诉我您的想法。

ClickOutsideBehavior

import ReactDOM from 'react-dom';

export default class ClickOutsideBehavior {

  constructor({component, appContainer, onClickOutside}) {

    // Can I extend the passed component's lifecycle events from here?
    this.component = component;
    this.appContainer = appContainer;
    this.onClickOutside = onClickOutside;
  }

  enable() {

    this.appContainer.addEventListener('click', this.handleDocumentClick);
  }

  disable() {

    this.appContainer.removeEventListener('click', this.handleDocumentClick);
  }

  handleDocumentClick = (event) => {

    const area = ReactDOM.findDOMNode(this.component);

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

样品用量

import React, {Component} from 'react';
import {APP_CONTAINER} from '../const';
import ClickOutsideBehavior from '../ClickOutsideBehavior';

export default class AddCardControl extends Component {

  constructor() {
    super();

    this.state = {
      toggledOn: false,
      text: ''
    };

    this.clickOutsideStrategy = new ClickOutsideBehavior({
      component: this,
      appContainer: APP_CONTAINER,
      onClickOutside: () => this.toggleState(false)
    });
  }

  componentDidMount () {

    this.setState({toggledOn: !!this.props.toggledOn});
    this.clickOutsideStrategy.enable();
  }

  componentWillUnmount () {
    this.clickOutsideStrategy.disable();
  }

  toggleState(isOn) {

    this.setState({toggledOn: isOn});
  }

  render() {...}
}

笔记

我想存储传递的component生命周期挂钩,并使用与之类似的方法来覆盖它们:

const baseDidMount = component.componentDidMount;

component.componentDidMount = () => {
  this.enable();
  baseDidMount.call(component)
}

component是传递给的构造函数的组件ClickOutsideBehavior

这将从用户的行为中删除启用/禁用样板,但是看起来不太好

在我的DROPDOWN案例中,Ben Bud的解决方案效果很好,但是我有一个单独的切换按钮和一个onClick处理程序。因此,外部点击逻辑与按钮onClick切换器冲突。这也是我通过传递按钮的ref来解决它的方法:

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

/**
 * Hook that triggers onClose when clicked outside of ref and buttonRef elements
 */
function useOutsideClicker(ref, buttonRef, onOutsideClick) {
  useEffect(() => {

    function handleClickOutside(event) {
      /* clicked on the element itself */
      if (ref.current && !ref.current.contains(event.target)) {
        return;
      }

      /* clicked on the toggle button */
      if (buttonRef.current && !buttonRef.current.contains(event.target)) {
        return;
      }

      /* If it's something else, trigger onClose */
      onOutsideClick();
    }

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

/**
 * Component that alerts if you click outside of it
 */
export default function DropdownMenu(props) {
  const wrapperRef = useRef(null);
  const buttonRef = useRef(null);
  const [dropdownVisible, setDropdownVisible] = useState(false);

  useOutsideClicker(wrapperRef, buttonRef, closeDropdown);

  const toggleDropdown = () => setDropdownVisible(visible => !visible);

  const closeDropdown = () => setDropdownVisible(false);

  return (
    <div>
      <button onClick={toggleDropdown} ref={buttonRef}>Dropdown Toggler</button>
      {dropdownVisible && <div ref={wrapperRef}>{props.children}</div>}
    </div>
  );
}

如果要使用此功能已经存在的很小的组件(gzip压缩为466 Byte),则可以签出该库react-outclick

关于库的好处是,它还使您可以检测组件外部和组件内部的单击。它还支持检测其他类型的事件。

如果要使用此功能已经存在的很小的组件(gzip压缩为466 Byte),则可以签出该库react-outclick它捕获了React组件或jsx元素之外的事件。

关于库的好处是,它还使您可以检测组件外部和组件内部的单击。它还支持检测其他类型的事件。

我知道这是一个老问题,但是我一直遇到这个问题,以简单的格式来解决这个问题时遇到了很多麻烦。因此,如果这可以使任何人的生活更轻松,请使用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>
 )
}

您可以通过简单的方法解决您的问题,我向您展示:

....

const [dropDwonStatus , setDropDownStatus] = useState(false)

const openCloseDropDown = () =>{
 setDropDownStatus(prev => !prev)
}

const closeDropDown = ()=> {
 if(dropDwonStatus){
   setDropDownStatus(false)
 }
}
.
.
.
<parent onClick={closeDropDown}>
 <child onClick={openCloseDropDown} />
</parent>

这对我有用,祝你好运;)

除了.contains外,还可以使用.closest方法。当您要检查单击是否在id =“ apple”元素之外时,我可以使用:

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

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

带钩的打字稿

注意:我正在使用带有React.createRef的React版本16.3。对于其他版本,请使用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>
  );
}

https://stackoverflow.com/a/42234988/9536897在移动模式下不起作用。

比您可以尝试的:

  // returns true if the element or one of its parents has the class classname
  hasSomeParentTheClass(element, classname) {
    if(element.target)
    element=element.target;
    
    if (element.className&& element.className.split(" ").indexOf(classname) >= 0) return true;
    return (
      element.parentNode &&
      this.hasSomeParentTheClass(element.parentNode, classname)
    );
  }
  componentDidMount() {
    const fthis = this;

    $(window).click(function (element) {
      if (!fthis.hasSomeParentTheClass(element, "myClass"))
        fthis.setState({ pharmacyFocus: null });
    });
  }
  • 在视图上,将className赋予您的特定元素。

我有一个案例,当我需要有条件地将孩子插入模态中时。这样,吼叫。

const [view, setView] = useState(VIEWS.SomeView)

return (
    <Modal onClose={onClose}>
      {VIEWS.Result === view ? (
        <Result onDeny={() => setView(VIEWS.Details)} />
      ) : VIEWS.Details === view ? (
        <Details onDeny={() => setView(VIEWS.Result) /> />
      ) : null}
    </Modal>
  )

所以!parent.contains(event.target)在这里不起作用,因为一旦分离孩子,父(模态)就不再包含event.target了。

我所拥有的解决方案(到目前为止有效,没有任何问题)是编写如下内容:

const listener = (event: MouseEvent) => {
   if (parentNodeRef && !event.path.includes(parentNodeRef)) callback()
}

如果parent包含已经分离的树中的元素,则不会触发回调。

编辑:
event.path是新的,并且不会在所有浏览器中退出。使用compoesedPath代替。

或者:

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

...

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

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

render(){return({this.node = node;}}>切换Popover {this.state.popupVisible &&(我是popover!)}});}}

这是有关此问题的精彩文章:
“在React组件之外处理点击”
https://larsgraubner.com/handle-outside-clicks-react/

本文地址:http://javascript.askforanswer.com/jiancereactzujianwaibudedianji.html
文章标签: ,   ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!