如何侦听组件外部的单击事件

All*_*tle 85 reactjs

我想在下拉组件外部发生单击时关闭下拉菜单.

我怎么做?

i_l*_*ots 71

使用生命周期方法向文档添加和删除事件侦听器.

React.createClass({
    handleClick: function (e) {
        if (this.getDOMNode().contains(e.target)) {
            return;
        }
    },

    componentWillMount: function () {
        document.addEventListener('click', this.handleClick, false);
    },

    componentWillUnmount: function () {
        document.removeEventListener('click', this.handleClick, false);
    }
});
Run Code Online (Sandbox Code Playgroud)

查看该组件的第48-54行:https://github.com/i-like-robots/react-tube-tracker/blob/91dc0129a1f6077bef57ea4ad9a860be0c600e9d/app/component/tube-tracker.jsx#L48-54

  • 如上所述this.getDOMNode()已过时.使用ReactDOM,如下所示:ReactDOM.findDOMNode(this).contains(e.target) (10认同)
  • 对于任何感兴趣的人,我在使用文档时遇到了stopPropagation的问题.如果您将事件监听器附加到窗口,它似乎解决了这个问题? (4认同)
  • 这确实会导致问题,因为@AllanHortle指出了这一点.对react事件的stopPropagation不会阻止文档事件处理程序接收事件. (3认同)

小智 57

在我添加的元素中mousedown,mouseup像这样:

onMouseDown={this.props.onMouseDown} onMouseUp={this.props.onMouseUp}
Run Code Online (Sandbox Code Playgroud)

然后在父母那里我这样做:

componentDidMount: function () {
    window.addEventListener('mousedown', this.pageClick, false);
},

pageClick: function (e) {
  if (this.mouseIsDownOnCalendar) {
      return;
  }

  this.setState({
      showCal: false
  });
},

mouseDownHandler: function () {
    this.mouseIsDownOnCalendar = true;
},

mouseUpHandler: function () {
    this.mouseIsDownOnCalendar = false;
}
Run Code Online (Sandbox Code Playgroud)

showCal是一个布尔值,true在我的情况下显示日历并false隐藏它.

  • 那些已经存在了很长时间,但是不会对android有好处,因为你需要立即对事件调用`preventDefault()`或者本机Android行为开始,React的预处理会干扰它.我写了https://www.npmjs.com/package/react-onclickoutside. (3认同)

j-a*_*j-a 17

查看事件的目标,如果事件直接在组件上或该组件的子项上,则单击是在内部.否则就在外面.

React.createClass({
    clickDocument: function(e) {
        var component = React.findDOMNode(this.refs.component);
        if (e.target == component || $(component).has(e.target).length) {
            // Inside of the component.
        } else {
            // Outside of the component.
        }

    },
    componentDidMount: function() {
        $(document).bind('click', this.clickDocument);
    },
    componentWillUnmount: function() {
        $(document).unbind('click', this.clickDocument);
    },
    render: function() {
        return (
            <div ref='component'>
                ...
            </div> 
        )
    }
});
Run Code Online (Sandbox Code Playgroud)

如果要在许多组件中使用它,那么使用mixin会更好:

var ClickMixin = {
    _clickDocument: function (e) {
        var component = React.findDOMNode(this.refs.component);
        if (e.target == component || $(component).has(e.target).length) {
            this.clickInside(e);
        } else {
            this.clickOutside(e);
        }
    },
    componentDidMount: function () {
        $(document).bind('click', this._clickDocument);
    },
    componentWillUnmount: function () {
        $(document).unbind('click', this._clickDocument);
    },
}
Run Code Online (Sandbox Code Playgroud)

请参阅此处的示例:https://jsfiddle.net/0Lshs7mg/1/

  • 我讨厌用React戳jQuery,因为它们是两个视图的框架. (2认同)

raz*_*ard 11

对于您的特定用例,目前接受的答案有点过度设计.如果您想要在用户单击下拉列表时监听,只需使用<select>组件作为父元素并onBlur为其附加处理程序.

这种方法的唯一缺点是它假设用户已经保持对元素的关注,并且它依赖于表单控件(如果你考虑到tab键也会聚焦和模糊元素,那么它可能是你想要的也可能不是你想要的) - 但这些缺点只是对更复杂的用例的限制,在这种情况下,可能需要更复杂的解决方案.

 var Dropdown = React.createClass({

   handleBlur: function(e) {
     // do something when user clicks outside of this element
   },

   render: function() {
     return (
       <select onBlur={this.handleBlur}>
         ...
       </select>
     );
   }
 });
Run Code Online (Sandbox Code Playgroud)

  • 如果它具有`tabIndex`属性,`onBlur` div也适用于div (10认同)

Gaj*_*jus 5

我为源自组件之外的事件(事件外反应)编写了一个通用事件处理程序.

实现本身很简单:

  • 安装组件时,会将事件处理程序附加到该window对象.
  • 发生事件时,组件会检查事件是否来自组件内部.如果没有,则onOutsideEvent在目标组件上触发.
  • 卸载组件时,将删除事件处理程序.
import React from 'react';
import ReactDOM from 'react-dom';

/**
 * @param {ReactClass} Target The component that defines `onOutsideEvent` handler.
 * @param {String[]} supportedEvents A list of valid DOM event names. Default: ['mousedown'].
 * @return {ReactClass}
 */
export default (Target, supportedEvents = ['mousedown']) => {
    return class ReactOutsideEvent extends React.Component {
        componentDidMount = () => {
            if (!this.refs.target.onOutsideEvent) {
                throw new Error('Component does not defined "onOutsideEvent" method.');
            }

            supportedEvents.forEach((eventName) => {
                window.addEventListener(eventName, this.handleEvent, false);
            });
        };

        componentWillUnmount = () => {
            supportedEvents.forEach((eventName) => {
                window.removeEventListener(eventName, this.handleEvent, false);
            });
        };

        handleEvent = (event) => {
            let target,
                targetElement,
                isInside,
                isOutside;

            target = this.refs.target;
            targetElement = ReactDOM.findDOMNode(target);
            isInside = targetElement.contains(event.target) || targetElement === event.target;
            isOutside = !isInside;



            if (isOutside) {
                target.onOutsideEvent(event);
            }
        };

        render() {
            return <Target ref='target' {... this.props} />;
        }
    }
};
Run Code Online (Sandbox Code Playgroud)

要使用该组件,需要使用更高阶的组件包装目标组件类声明,并定义要处理的事件:

import React from 'react';
import ReactDOM from 'react-dom';
import ReactOutsideEvent from 'react-outside-event';

class Player extends React.Component {
    onOutsideEvent = (event) => {
        if (event.type === 'mousedown') {

        } else if (event.type === 'mouseup') {

        }
    }

    render () {
        return <div>Hello, World!</div>;
    }
}

export default ReactOutsideEvent(Player, ['mousedown', 'mouseup']);
Run Code Online (Sandbox Code Playgroud)