React 컴포넌트 외부의 클릭 감지
이 기사 에서 설명하는 것처럼 클릭 이벤트가 구성 요소 외부에서 발생했는지 감지하는 방법을 찾고 있습니다. jQuery closest ()는 click 이벤트의 대상에 dom 요소가 상위 요소 중 하나인지 확인하는 데 사용됩니다. 일치하는 경우 click 이벤트는 하위 중 하나에 속하므로 구성 요소 외부에있는 것으로 간주되지 않습니다.
그래서 내 구성 요소에서 클릭 핸들러를 창에 연결하고 싶습니다. 처리기가 시작되면 대상을 내 구성 요소의 dom 자식과 비교해야합니다.
click 이벤트에는 이벤트가 이동 한 dom 경로를 보유하는 것으로 보이는 "path"와 같은 속성이 포함되어 있습니다. 나는 무엇을 비교할 것인지 또는 어떻게 그것을 가장 잘 횡단 할 것인지 잘 모르겠습니다.
다음 솔루션은 ES6을 사용하며 방법을 통해 참조 설정뿐만 아니라 바인딩에 대한 모범 사례를 따릅니다.
그것을 실제로 보려면 :
클래스 구현 :
import React, { Component } from 'react';
/**
* Component that alerts if you click outside of it
*/
export default class OutsideAlerter extends Component {
constructor(props) {
super(props);
this.setWrapperRef = this.setWrapperRef.bind(this);
this.handleClickOutside = this.handleClickOutside.bind(this);
}
componentDidMount() {
document.addEventListener('mousedown', this.handleClickOutside);
}
componentWillUnmount() {
document.removeEventListener('mousedown', this.handleClickOutside);
}
/**
* Set the wrapper ref
*/
setWrapperRef(node) {
this.wrapperRef = node;
}
/**
* Alert if clicked on outside of element
*/
handleClickOutside(event) {
if (this.wrapperRef && !this.wrapperRef.contains(event.target)) {
alert('You clicked outside of me!');
}
}
render() {
return <div ref={this.setWrapperRef}>{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) {
/**
* Alert if clicked on outside of element
*/
function handleClickOutside(event) {
if (ref.current && !ref.current.contains(event.target)) {
alert("You clicked outside of me!");
}
}
useEffect(() => {
// Bind the event listener
document.addEventListener("mousedown", handleClickOutside);
return () => {
// Unbind the event listener on clean up
document.removeEventListener("mousedown", handleClickOutside);
};
});
}
/**
* 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 참조).
당신은 또한 자바 스크립트를 통해 그것을 할 수 있습니다 element.setAttribute('tabindex',0)
이것은 사용자 정의 드롭 다운 메뉴를 만들기 위해 내가 사용했던 것입니다.
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>
);
}
});
여기에서 많은 방법을 시도한 후 github.com/Pomax/react-onclickoutside 를 사용하기로 결정했습니다 .
npm을 통해 모듈을 설치하고 내 구성 요소로 가져 왔습니다.
import onClickOutside from 'react-onclickoutside'
그런 다음 컴포넌트 클래스에서 handleClickOutside
메소드를 정의했습니다 .
handleClickOutside = () => {
console.log('onClickOutside() method called')
}
그리고 내 구성 요소를 내보낼 때 다음과 같이 포장했습니다 onClickOutside()
.
export default onClickOutside(NameOfComponent)
그게 다야.
나는 같은 문제에 갇혀 있었다. 나는 파티에 조금 늦었지만 나에게는 이것이 정말 좋은 해결책이다. 잘만되면 그것은 다른 누군가에게 도움이 될 것입니다. 에서 가져와야합니다 findDOMNode
.react-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
});
}
}
반응 고리 접근 (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>
);
}
codesandbox 예제를 찾으 십시오.
나는 벤 알퍼트에 대한 해결책 덕분에 발견 discuss.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.
}
},
도움이 되었기를 바랍니다.
반응 고리가있는 재사용 가능한 솔루션 (16.8 +)
외부 클릭 알림 후크 작성 :
function useOuterClickNotifier(onOuterClick, innerRef) {
useEffect(
() => {
// only add listener, if the element exists
if (innerRef.current) {
document.addEventListener("click", handleClick);
}
// unmount previous first in case inputs have changed
return () => document.removeEventListener("click", handleClick);
function handleClick(e) {
innerRef.current && !innerRef.current.contains(e.target) && onOuterClick(e);
}
},
[onOuterClick, innerRef] // invoke again, if inputs have changed
);
}
다음과 같은 구성 요소에서 후크를 사용하십시오.
const InnerComp = () => {
const innerRef = useRef(null);
useOuterClickNotifier(
// if you want to optimize performance a bit,
// don't provide an anonymous function here
// See link down under (*1)
e => alert("clicked outside of this component!"),
innerRef
);
return (
<div ref={innerRef}>
inside component
</div>
);
}
* 1 효과를 건너 뛰어 성능 최적화
그런 다음 사용 사례에 따라 외부 클릭 콜백에서 무언가를 수행 할 수 alert("clicked outside of this component!")
있습니다. 예를 들어 useState
Hook로 상태를 설정 하거나 주어진 콜백을 호출하십시오.
클래스 솔루션에 비해 장점 :
- 외부 클릭 부작용 로직을 캡슐화 ( 문제 분리 )하고 소비되는 구성 요소를 쉽게 처리 할 수 있습니다 .
useOuterClickNotifier
후크는 클래스 솔루션 ( Link ) 과 같은 래퍼 구성 요소 / 렌더러 소품 없이도 모든 구성 요소에서 재사용 할 수 있습니다.- 장기적으로 React 팀은 후크가 사람들이 React 컴포넌트를 작성하는 주요 방법이 될 것으로 기대합니다 ( Link ).
단점 :
추가 정보 : 클릭 가능한 요소가있는 iOS 쿼크 (영향 mousedown
, click
)
IOS는 특정 요소 만 클릭 할 수있는 것으로 취급합니다. quirksmode , SO answer 및 here 에 대한 자세한 정보 입니다. 이 동작을 피하려면 다른 외부 클릭 리스너 요소를 선택하십시오 (을 포함하여 위쪽에는 없음 body
). 예를 들어 반응 루트의 클릭을 <div id="root"></div>
대신 등록 하고 높이를 전체 뷰포트로 확장 할 수 있습니다 ( IOS 코드 및 상자 참조 ). 또는 더 좋고 반응이 좋은 방법 : 전체를 피하고 useOuterClickNotifier
외부 클릭을 등록하는 데 사용할 수 있는 명시 적 요소를 Hook에 전달하십시오 .
CSS 미디어 쿼리를 사용한 추가 대안에 대해서는 힌트를위한 @Omar와 @Kostas Sarantopoulos에게 감사의 말을 전한다 (주석 참조).
희망이 도움이됩니다.
[업데이트] 후크를 사용한 React ^ 16.8 솔루션
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;
반응이있는 솔루션 ^ 16.3 :
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;
내 접근 방식은 다음과 같습니다 (demo- 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에는 자체 인공 이벤트 핸들러가 있기 때문에 문서를 이벤트 리스너의 기반으로 사용할 수 없습니다. e.stopPropagation()
React가 문서 자체를 사용하므로이 작업을 수행 해야 합니다. 예를 들어 document.querySelector('body')
대신 사용하는 경우 . 반응 링크에서 클릭을 방지 할 수 있습니다. 다음은 클릭 외부를 구현하고 닫는 방법의 예입니다.
이것은 ES6 과 React 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;
스타일이 지정된 구성 요소를 사용하는 경우 Ben Bud 에서 허용 된 답변을 확장하기 위해 해당 방식으로 참조를 전달하면 "this.wrapperRef.contains is a function"과 같은 오류가 발생합니다.
주석에서 스타일이 지정된 구성 요소를 div로 감싸고 참조를 전달하는 제안 된 수정 프로그램이 작동합니다. 자신에 그런 말로 미루어 문서 는 이미 그 이유와 스타일-구성 요소에서 심판의 적절한 사용을 설명합니다 :
스타일 지정된 구성 요소에 참조 소품을 전달하면 기본 DOM 노드가 아닌 StyledComponent 래퍼의 인스턴스가 제공됩니다. 심판이 작동하는 방식 때문입니다. 랩퍼에서 포커스와 같은 DOM 메소드를 직접 호출 할 수 없습니다. 랩핑 된 실제 DOM 노드에 대한 참조를 얻으려면 대신 콜백을 innerRef prop에 전달하십시오.
이렇게 :
<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 }}
/>
)
}
다른 모든 답변에 대한 가장 큰 관심사는 루트 / 부모 다운에서 클릭 이벤트를 필터링해야한다는 것입니다. 가장 쉬운 방법은 위치 : 고정, 드롭 다운 뒤에 z- 인덱스 1로 형제 요소를 설정하고 동일한 구성 요소 내부의 고정 요소에서 클릭 이벤트를 처리하는 것입니다. 주어진 구성 요소에 모든 것을 중앙 집중적으로 유지합니다.
예제 코드
#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;
}
}
절대 위치 지정이 필요한 사람들을 위해, 내가 선택한 간단한 옵션은 전체 페이지를 투명한 배경으로 덮는 스타일의 래퍼 구성 요소를 추가하는 것입니다. 그런 다음이 요소에 onClick을 추가하여 내부 구성 요소를 닫을 수 있습니다.
<div style={{
position: 'fixed',
top: '0', right: '0', bottom: '0', left: '0',
zIndex: '1000',
}} onClick={() => handleOutsideClick()} >
<Content style={{position: 'absolute'}}/>
</div>
콘텐츠에 클릭 핸들러를 추가하면 바로 이벤트가 상단 div로 전파되어 handlerOutsideClick이 트리거됩니다. 원하는 동작이 아닌 경우 처리기에서 이벤트 생성을 중지하면됩니다.
<Content style={{position: 'absolute'}} onClick={e => {
e.stopPropagation();
desiredFunctionCall();
}}/>
`
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>
);
}
}
작업을 훌륭하게 수행했습니다.
나는 이것을 수행하고 반응 ^ 16.3이 필요한 심판 처리에 대한 React 공식 문서를 따라 부분적으로 수행 했습니다 . 이것은 다른 제안을 시도한 후에 나를 위해 일한 유일한 것입니다 ...
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 => {
if (this.inputRef.current === e.target) {
return;
}
this.handleclickOutside();
};
handleClickOutside(){
...***code to handle what to do when clicked outside***...
}
render(){
return(
<div>
...***code for what's outside***...
<span ref={this.inputRef}>
...***code for what's "inside"***...
</span>
...***code for what's outside***
)}}
전략의 예
구성 요소 주위에 래퍼를 만들어 동일한 작업을 수행하는 데 제공되는 솔루션이 마음에 듭니다.
이것이 더 많은 행동이기 때문에 전략을 생각하고 다음을 생각해 냈습니다.
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
.
이것은이 동작의 사용자로부터 활성화 / 비활성화 상용구를 제거하지만 아주 좋아 보이지는 않습니다.
아래 기사에서 이것을 찾았습니다.
render () {return ({this.node = node;}}> 팝 오버 토글 {this.state.popupVisible && (나는 팝 오버입니다!)}); }}
다음은이 문제에 대한 유용한 기사입니다. "React 구성 요소 외부에서 클릭 처리" https://larsgraubner.com/handle-outside-clicks-react/
onClick
최상위 컨테이너에 핸들러를 추가 하고 사용자가 클릭 할 때마다 상태 값을 증가시킵니다. 해당 값을 관련 구성 요소에 전달하고 값이 변경 될 때마다 작업을 수행 할 수 있습니다.
이 경우 값이 변경 this.closeDropdown()
될 때마다 호출 합니다 clickCount
.
incrementClickCount
내 방법 화재 .app
용기가 아니라는 .dropdown
우리가 사용하기 때문에 event.stopPropagation()
이벤트 버블 링을 방지 할 수 있습니다.
코드가 다음과 같이 보일 수 있습니다.
class App extends Component {
constructor(props) {
super(props);
this.state = {
clickCount: 0
};
}
incrementClickCount = () => {
this.setState({
clickCount: this.state.clickCount + 1
});
}
render() {
return (
<div className="app" onClick={this.incrementClickCount}>
<Dropdown clickCount={this.state.clickCount}/>
</div>
);
}
}
class Dropdown extends Component {
constructor(props) {
super(props);
this.state = {
open: false
};
}
componentDidUpdate(prevProps) {
if (this.props.clickCount !== prevProps.clickCount) {
this.closeDropdown();
}
}
toggleDropdown = event => {
event.stopPropagation();
return (this.state.open) ? this.closeDropdown() : this.openDropdown();
}
render() {
return (
<div className="dropdown" onClick={this.toggleDropdown}>
...
</div>
);
}
}
이벤트 리스너를 사용하여 드롭 다운에 '초점' 솔루션을 작동 시키려면 onClick 대신 onMouseDown 이벤트로 추가하십시오 . 그렇게하면 이벤트가 시작되고 팝업이 다음과 같이 닫힙니다.
<TogglePopupButton
onClick = { this.toggleDropup }
tabIndex = '0'
onBlur = { this.closeDropup }
/>
{ this.state.isOpenedDropup &&
<ul className = { dropupList }>
{ this.props.listItems.map((item, i) => (
<li
key = { i }
onMouseDown = { item.eventHandler }
>
{ item.itemName}
</li>
))}
</ul>
}
import ReactDOM from 'react-dom' ;
class SomeComponent {
constructor(props) {
// First, add this to your constructor
this.handleClickOutside = this.handleClickOutside.bind(this);
}
componentWillMount() {
document.addEventListener('mousedown', this.handleClickOutside, false);
}
// Unbind event on unmount to prevent leaks
componentWillUnmount() {
window.removeEventListener('mousedown', this.handleClickOutside, false);
}
handleClickOutside(event) {
if(!ReactDOM.findDOMNode(this).contains(event.path[0])){
console.log("OUTSIDE");
}
}
}
나는 모든 경우에 대한 해결책을 만들었습니다.
상위 구성 요소를 사용하여 외부에서 클릭을 수신하려는 구성 요소를 랩핑해야합니다.
이 컴포넌트 예제에는 함수를 수신하는 "onClickedOutside"소품이 하나만 있습니다.
ClickedOutside.js
import React, { Component } from "react";
export default class ClickedOutside extends Component {
componentDidMount() {
document.addEventListener("mousedown", this.handleClickOutside);
}
componentWillUnmount() {
document.removeEventListener("mousedown", this.handleClickOutside);
}
handleClickOutside = event => {
// IF exists the Ref of the wrapped component AND his dom children doesnt have the clicked component
if (this.wrapperRef && !this.wrapperRef.contains(event.target)) {
// A props callback for the ClikedClickedOutside
this.props.onClickedOutside();
}
};
render() {
// In this piece of code I'm trying to get to the first not functional component
// Because it wouldn't work if use a functional component (like <Fade/> from react-reveal)
let firstNotFunctionalComponent = this.props.children;
while (typeof firstNotFunctionalComponent.type === "function") {
firstNotFunctionalComponent = firstNotFunctionalComponent.props.children;
}
// Here I'm cloning the element because I have to pass a new prop, the "reference"
const children = React.cloneElement(firstNotFunctionalComponent, {
ref: node => {
this.wrapperRef = node;
},
// Keeping all the old props with the new element
...firstNotFunctionalComponent.props
});
return <React.Fragment>{children}</React.Fragment>;
}
}
UseOnClickOutside 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>
);
};
@ pau1fitzgerald의 답변에서 부분적으로 영감을 얻었습니다.
위의 답변 중 어느 것도 나를 위해 일하지 않았으므로 여기에 내가 한 일이 있습니다.
import React, {Component}에서 'react';
/**
* Component that alerts if you click outside of it
*/
export default class OutsideAlerter extends Component {
constructor(props) {
super(props);
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 (!event.path || !event.path.filter(item => item.className=='classOfAComponent').length) {
alert('You clicked outside of me!');
}
}
render() {
return <div>{this.props.children}</div>;
}
}
OutsideAlerter.propTypes = {
children: PropTypes.element.isRequired,
};
본문에 더블 클릭 핸들러를 설치하고이 요소에 다른 핸들러를 설치할 수 있습니다. 이 요소의 핸들러에서 이벤트가 전파되지 않도록 false를 리턴하십시오. 따라서 요소를 두 번 클릭하면 요소에 걸리고 본문의 처리기로 전파되지 않습니다. 그렇지 않으면 본문의 핸들러에 의해 포착됩니다.
업데이트 : 실제로 이벤트 전파를 방지하지 않으려면 가장 가까운 것을 사용하여 요소 또는 자녀 중 하나에서 클릭이 발생했는지 확인하십시오.
<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>
참고 URL : https://stackoverflow.com/questions/32553158/detect-click-outside-react-component
'Programing' 카테고리의 다른 글
파이썬에서 부모 디렉토리를 어떻게 얻습니까? (0) | 2020.03.16 |
---|---|
장치의 실행 화면에서 UIImageView 이미지가 누락 됨 (0) | 2020.03.16 |
data.frame에서 단일 열의 이름을 바꾸는 방법은 무엇입니까? (0) | 2020.03.16 |
SearchView로 RecyclerView를 필터링하는 방법 (0) | 2020.03.16 |
JavaScript에서 Deferred, Promise 및 Future의 차이점은 무엇입니까? (0) | 2020.03.16 |