program story

구성 요소 외부에있는 클릭 이벤트를 수신하는 방법

inputbox 2020. 9. 16. 07:41
반응형

구성 요소 외부에있는 클릭 이벤트를 수신하는 방법


드롭 다운 구성 요소 외부에서 클릭이 발생하면 드롭 다운 메뉴를 닫고 싶습니다.

어떻게하나요?


추가 한 요소에서 다음 mousedownmouseup같이합니다.

onMouseDown={this.props.onMouseDown} onMouseUp={this.props.onMouseUp}

그런 다음 부모에서 다음을 수행합니다.

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

showCal부울입니다 때 true내 경우에는 공연 일정 및 false가죽 그것.


수명주기 메서드를 사용하여 문서에 이벤트 리스너를 추가 및 제거합니다.

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

이 구성 요소의 48-54 행을 확인하십시오. https://github.com/i-like-robots/react-tube-tracker/blob/91dc0129a1f6077bef57ea4ad9a860be0c600e9d/app/component/tube-tracker.jsx#L48-54


이벤트의 대상을 살펴 봅니다. 이벤트가 구성 요소에 직접 있거나 해당 구성 요소의 자식에 있으면 클릭이 내부에있는 것입니다. 그렇지 않으면 밖에 있었다.

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

이것이 많은 구성 요소에서 사용되는 경우 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);
    },
}

여기에서 예를 참조하십시오 : https://jsfiddle.net/0Lshs7mg/1/


특정 사용 사례의 경우 현재 허용되는 대답은 약간의 과잉 엔지니어링입니다. 사용자가 드롭 다운 목록을 클릭 할 때 수신 대기하려면 <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>
     );
   }
 });

컴포넌트 외부에서 발생하는 이벤트에 대한 일반 이벤트 핸들러 인 react-outside-event를 작성했습니다 .

구현 자체는 간단합니다.

  • 컴포넌트가 마운트되면 이벤트 핸들러가 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} />;
        }
    }
};

구성 요소를 사용하려면 상위 구성 요소를 사용하여 대상 구성 요소 클래스 선언을 래핑하고 처리 할 이벤트를 정의해야합니다.

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']);

I voted up one of the answers even though it didn't work for me. It ended up leading me to this solution. I changed the order of operations slightly. I listen for mouseDown on the target and mouseUp on the target. If either of those return TRUE, we don't close the modal. As soon as a click is registered, anywhere, those two booleans { mouseDownOnModal, mouseUpOnModal } are set back to false.

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

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

_handlePageClick(e) {
    var wasDown = this.mouseDownOnModal;
    var wasUp = this.mouseUpOnModal;
    this.mouseDownOnModal = false;
    this.mouseUpOnModal = false;
    if (!wasDown && !wasUp)
        this.close();
},

_handleMouseDown() {
    this.mouseDownOnModal = true;
},

_handleMouseUp() {
    this.mouseUpOnModal = true;
},

render() {
    return (
        <Modal onMouseDown={this._handleMouseDown} >
               onMouseUp={this._handleMouseUp}
            {/* other_content_here */}
        </Modal>
    );
}

This has the advantage that all the code rests with the child component, and not the parent. It means that there's no boilerplate code to copy when reusing this component.


  1. Create a fixed layer that spans the whole screen (.backdrop).
  2. Have the target element (.target) outside the .backdrop element and with a greater stacking index (z-index).

Then any click on the .backdrop element will be considered "outside of the .target element".

.click-overlay {
    position: fixed;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
    z-index: 1;
}

.target {
    position: relative;
    z-index: 2;
}

You could use refs to achieve this, something like the following should work.

Add the ref to your element:

<div ref={(element) => { this.myElement = element; }}></div>

You can then add a function for handling the click outside of the element like so:

handleClickOutside(e) {
  if (!this.myElement.contains(e)) {
    this.setState({ myElementVisibility: false });
  }
}

Then finally, add and remove the event listeners on will mount and will unmount.

componentWillMount() {
  document.addEventListener('click', this.handleClickOutside, false);  // assuming that you already did .bind(this) in constructor
}

componentWillUnmount() {
  document.removeEventListener('click', this.handleClickOutside, false);  // assuming that you already did .bind(this) in constructor
}

Super late to the party, but I've had success with setting a blur event on the parent element of the dropdown with the associated code to close the dropdown, and also attaching a mousedown listener to the parent element that checks if the dropdown is open or not, and will stop the event propagation if it is open so that the blur event won't be triggered.

Since the mousedown event bubbles up this will prevent any mousedown on children from causing a blur on the parent.

/* Some react component */
...

showFoo = () => this.setState({ showFoo: true });

hideFoo = () => this.setState({ showFoo: false });

clicked = e => {
    if (!this.state.showFoo) {
        this.showFoo();
        return;
    }
    e.preventDefault()
    e.stopPropagation()
}

render() {
    return (
        <div 
            onFocus={this.showFoo}
            onBlur={this.hideFoo}
            onMouseDown={this.clicked}
        >
            {this.state.showFoo ? <FooComponent /> : null}
        </div>
    )
}

...

e.preventDefault() shouldn't have to be called as far as I can reason but firefox doesn't play nice without it for whatever reason. Works on Chrome, Firefox, and Safari.


I found a simpler way about this.

You just need to add onHide(this.closeFunction) on the modal

<Modal onHide={this.closeFunction}>
...
</Modal>

Assuming you have a function to close the modal.


Use the excellent react-onclickoutside mixin:

npm install --save react-onclickoutside

And then

var Component = React.createClass({
  mixins: [
    require('react-onclickoutside')
  ],
  handleClickOutside: function(evt) {
    // ...handling code goes here... 
  }
});

참고URL : https://stackoverflow.com/questions/23821768/how-to-listen-for-click-events-that-are-outside-of-a-component

반응형