पता लगाएँ प्रतिक्रिया घटक के बाहर क्लिक करें


410

मैं इस लेख में वर्णित एक घटक के बाहर एक क्लिक घटना होने पर पता लगाने का एक तरीका खोज रहा हूं । jQuery के निकटतम () का उपयोग यह देखने के लिए किया जाता है कि किसी क्लिक इवेंट के लक्ष्य में उसके माता-पिता में से एक के रूप में डोम तत्व है या नहीं। अगर मैच होता है तो क्लिक इवेंट बच्चों में से एक का होता है और इस तरह इसे घटक के बाहर नहीं माना जाता है।

इसलिए मेरे कंपोनेंट में मैं विंडो पर क्लिक हैंडलर संलग्न करना चाहता हूं। जब हैंडलर फायर करता है तो मुझे अपने घटक के डोम बच्चों के साथ लक्ष्य की तुलना करने की आवश्यकता होती है।

क्लिक इवेंट में "पथ" जैसे गुण होते हैं जो लगता है कि डोम ने पथ को पकड़ लिया है। मुझे यकीन नहीं है कि इसकी तुलना कैसे की जाए या इसे सबसे बेहतर तरीके से कैसे पार किया जाए, और मैं सोच रहा हूं कि किसी ने पहले ही इसे एक चतुर उपयोगिता फ़ंक्शन में डाल दिया होगा ... नहीं?


क्या आप विंडो के बजाय पैरेंट को क्लिक हैंडलर संलग्न कर सकते हैं?
जे। मार्क स्टीवंस

यदि आप उस अभिभावक को एक क्लिक हैंडलर संलग्न करते हैं जो आप जानते हैं कि उस तत्व या उनके बच्चों में से एक पर क्लिक किया गया है, लेकिन मुझे क्लिक किए जाने वाले अन्य सभी स्थानों का पता लगाने की आवश्यकता है , इसलिए हैंडलर को खिड़की से संलग्न करना होगा।
थाइज कोसरेलमैन

मैंने पिछली प्रतिक्रिया के बाद लेख को देखा। कैसे शीर्ष घटक में एक क्लिकस्टैट स्थापित करने और बच्चों से क्लिक क्रियाओं को पारित करने के बारे में। तब आप बच्चों में खुले राज्य का प्रबंधन करने के लिए प्रॉप्स की जांच करेंगे।
जे। मार्क स्टीवंस

शीर्ष घटक मेरा ऐप होगा। लेकिन सुनने का घटक कई स्तरों गहरा है और डोम में कोई सख्त स्थिति नहीं है। मैं संभवतः अपने ऐप में सभी घटकों के लिए क्लिक हैंडलर्स नहीं जोड़ सकता क्योंकि उनमें से एक को यह जानने में रुचि है कि क्या आपने इसके बाहर कहीं क्लिक किया है। अन्य घटकों को इस तर्क के बारे में पता नहीं होना चाहिए क्योंकि इससे भयानक निर्भरता और बॉयलरप्लेट कोड बन जाएगा।
थाइज कोसरेलमैन

5
मैं आपको एक बहुत अच्छा काम करने की सलाह देना चाहूंगा। AirBnb द्वारा निर्मित: github.com/airbnb/react-outside-click-handler
Osoian Marcel

जवाबों:


681

निम्न समाधान ईएस 6 का उपयोग करता है और एक विधि के माध्यम से रेफ को सेट करने के साथ-साथ बाध्यकारी के लिए सर्वोत्तम प्रथाओं का पालन करता है।

इसे कार्रवाई में देखने के लिए:

कक्षा कार्यान्वयन:

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

3
हालांकि आप इसका परीक्षण कैसे करते हैं? कैसे आप एक मान्य event.target को भेजें ।ClickOutside फ़ंक्शन को भेजें?
csilk

5
आप document.addEventListenerयहां के बजाय रिएक्ट की सिंथेटिक घटनाओं का उपयोग कैसे कर सकते हैं?
राल्फ डेविड एबरनेथी

9
@ polkovnikov.ph 1. contextनिर्माण में एक तर्क के रूप में केवल आवश्यक है यदि इसका उपयोग किया जाता है। इस मामले में इसका उपयोग नहीं किया जा रहा है। कारण यह है कि प्रतिक्रिया टीम ने निर्माता propsमें एक तर्क के रूप में होने की सिफारिश की है क्योंकि this.propsकॉल करने से पहले उपयोग super(props)अपरिभाषित होगा और त्रुटियों को जन्म दे सकता है। contextअभी भी आंतरिक नोड्स पर उपलब्ध है, इसका पूरा उद्देश्य है context। इस तरह से आपको इसे घटक से घटक की तरह नीचे पारित करने की आवश्यकता नहीं है, जैसे कि आप प्रॉप्स के साथ करते हैं। 2. यह सिर्फ एक शैलीगत प्राथमिकता है, और इस मामले में बहस करने का वारंट नहीं है।
बेन बड

7
मुझे निम्नलिखित त्रुटि मिलती है: "this.wrapperRef.contains एक फ़ंक्शन नहीं है"
बोगडान

5
@ बोगदान, मुझे स्टाइल-कंपोनेंट का उपयोग करते समय एक ही त्रुटि मिल रही थी। शीर्ष स्तर पर <div> का उपयोग करने पर विचार करें
user3040068

149

यहाँ समाधान है कि कंटेनर के लिए घटनाओं को संलग्न किए बिना मेरे लिए सबसे अच्छा काम किया है:

कुछ HTML तत्वों में " फ़ोकस " के रूप में जाना जाता है , उदाहरण के लिए इनपुट तत्व हो सकते हैं। वे तत्व धब्बा घटना पर भी प्रतिक्रिया देंगे, जब वे उस फोकस को खो देंगे।

किसी भी तत्व को ध्यान केंद्रित करने की क्षमता देने के लिए, बस यह सुनिश्चित करें कि इसकी टैबइंडेक्स विशेषता -1 के अलावा किसी अन्य चीज़ पर सेट है। नियमित HTML में जो tabindexविशेषता सेट करके होगा , लेकिन प्रतिक्रिया में आपको 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>
        );
    }
});

10
क्या यह एक्सेसिबिलिटी की समस्या नहीं पैदा करेगा? चूँकि आप एक अतिरिक्त तत्व को फोकस करने के लिए बनाते हैं, जब यह पता चलता है कि यह / बाहर फोकस में जा रहा है, लेकिन सहभागिता ड्रॉपडाउन मानों पर होनी चाहिए?
थाइज कोसरेलमैन

2
यह बहुत दिलचस्प दृष्टिकोण है। मेरी स्थिति के लिए पूरी तरह से काम किया है, लेकिन मुझे यह जानने में दिलचस्पी होगी कि यह एक्सेसिबिलिटी को कैसे प्रभावित कर सकता है।
क्लिनोर

17
मैं एक हद तक इस "काम", एक शांत थोड़ा हैक है। मेरा मुद्दा यह है कि मेरी ड्रॉप डाउन सामग्री display:absoluteइतनी है कि ड्रॉप डाउन माता-पिता के तलाक के आकार को प्रभावित नहीं करेगा। इसका मतलब है कि जब मैं ड्रॉपडाउन में किसी आइटम पर क्लिक करता हूं, तो ऑनब्लर फायर करता है।
डेव

2
मेरे पास इस दृष्टिकोण के साथ समस्या थी, ड्रॉपडाउन सामग्री में कोई भी तत्व ऑनक्लिक जैसे घटनाओं को आग नहीं देता है।
अणिमासोला

8
यदि ड्रॉपडाउन सामग्री में उदाहरण के लिए प्रपत्र इनपुट जैसे कुछ फ़ोकस करने योग्य तत्व हैं, तो आपको कुछ अन्य समस्याओं का सामना करना पड़ सकता है। वे आपका ध्यान चुरा लेंगे, onBlur आपके ड्रॉपडाउन कंटेनर पर ट्रिगर हो expanded जाएगा और झूठे पर सेट हो जाएगा।
कामागाटोस

106

मैं उसी मुद्दे पर अड़ा रहा। मैं यहां पार्टी के लिए थोड़ा लेट हूं, लेकिन मेरे लिए यह वास्तव में अच्छा समाधान है। उम्मीद है कि यह किसी और की मदद होगी। आप आयात करने की जरूरत 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>
    );

}

यहां एक कोडैंडबॉक्स उदाहरण ढूंढें ।


1
@LeeHanKyeol पूरी तरह से नहीं - यह जवाब इवेंट हैंडलिंग के कैप्चर चरण के दौरान ईवेंट हैंडलर को आमंत्रित करता है, जबकि बबल चरण के दौरान इवेंट हैंडलर को आमंत्रित करने से जुड़ा उत्तर।
स्टीवेयज

3
यह स्वीकृत उत्तर होना चाहिए। पूर्ण स्थिति के साथ ड्रॉपडाउन मेनू के लिए पूरी तरह से काम किया।
dishwasherWithProgrammingSkill

1
ReactDOM.findDOMNodeऔर पदावनत किया जाता है, refकॉलबैक का उपयोग करना चाहिए : github.com/yannickcr/eslint-plugin-react/issues/…
dain

2
महान और साफ समाधान
करीम

1
यह मेरे लिए काम करता है, हालांकि मुझे 200ms के बाद दृश्यमान 'सही' पर रीसेट करने के लिए आपके घटक को हैक करना पड़ा (अन्यथा मेरा मेनू फिर कभी नहीं दिखाता)। धन्यवाद!
ली मोए

87

यहाँ कई तरीकों की कोशिश करने के बाद, मैंने यह तय करने की वजह से github.com/Pomax/react-onclickoutside का उपयोग करने का निर्णय लिया ।

मैंने npm के माध्यम से मॉड्यूल को स्थापित किया और इसे अपने घटक में आयात किया:

import onClickOutside from 'react-onclickoutside'

फिर, मेरे घटक वर्ग में मैंने handleClickOutsideविधि को परिभाषित किया :

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

और मेरे घटक का निर्यात करते समय मैंने इसे इसमें लपेटा onClickOutside():

export default onClickOutside(NameOfComponent)

बस।


2
पाब्लो द्वारा प्रस्तावित tabIndex/ onBlurदृष्टिकोण के ऊपर क्या कोई ठोस लाभ हैं ? कार्यान्वयन कैसे काम करता है, और इसका व्यवहार onBlurदृष्टिकोण से कैसे भिन्न होता है ?
मार्क अमेरी

4
एक समर्पित घटक को तब टैबइंडेक्स हैक का उपयोग करके उपयोगकर्ता के लिए बेहतर है। इसे अपवित्र करना!
अतुल यादव

5
@ मार्कीमेरी - मैंने tabIndex/onBlurदृष्टिकोण पर एक टिप्पणी रखी । यह काम नहीं करता है जब ड्रॉपडाउन होता है position:absolute, जैसे कि अन्य सामग्री पर मंडराते हुए मेनू।
ब्यू स्मिथ

4
टैबइंडेक्स पर इस का उपयोग करने का एक और फायदा यह है कि अगर आप किसी बाल तत्व पर ध्यान केंद्रित करते हैं तो टैबइंडेक्स समाधान भी एक धब्बा घटना को आग लगा देता है
जेमर जोन्स

1
@ ब्यूस्मिथ - बहुत बहुत धन्यवाद! मेरा दिन बचाया!
मिका

38

मुझे चर्चा के दौरान बेन अल्परट के लिए एक समाधान मिला ।reactjs.org । सुझाए गए दृष्टिकोण दस्तावेज़ को एक हैंडलर संलग्न करता है लेकिन यह समस्याग्रस्त हो गया है। मेरे पेड़ के घटकों में से एक पर क्लिक करने के परिणामस्वरूप एक रेंडर किया गया जिसने अपडेट पर क्लिक किए गए तत्व को हटा दिया। क्योंकि रिएक्टर से रेंडर करने से पहले दस्तावेज़ बॉडी हैंडलर कहलाता है, इसलिए तत्व को पेड़ के "अंदर" के रूप में नहीं पाया गया।

इसका समाधान एप्लिकेशन रूट तत्व पर हैंडलर को जोड़ना था।

मुख्य:

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

8
यह मेरे लिए रिएक्ट 0.14.7 के साथ और अधिक काम नहीं करता है - शायद रिएक्ट कुछ बदल गया है, या हो सकता है कि मैंने रिएक्ट के सभी परिवर्तनों के लिए कोड को स्वीकार करते समय एक त्रुटि की। मैं इसके बजाय github.com/Pomax/react-onclickoutside का उपयोग कर रहा हूं जो आकर्षण की तरह काम करता है।
निकोल

1
हम्म। मुझे कोई कारण नहीं दिख रहा है कि यह क्यों काम करना चाहिए। ऐप के मूल डोम हैंड पर एक हैंडलर को एक हैंडलर द्वारा दूसरे हैंडलर द्वारा ट्रिगर न किए जाने से पहले आग लगने की गारंटी क्यों दी जाए document?
मार्क एमी

1
एक शुद्ध यूएक्स बिंदु: mousedownशायद clickयहां से बेहतर हैंडलर होगा । अधिकांश अनुप्रयोगों में, क्लोज-मेन्यू-बाय-क्लिक-आउट बिहेवियर उस क्षण होता है जब आप माउस छोड़ते हैं, जब आप रिलीज़ नहीं करते हैं। उदाहरण के लिए, स्टैक ओवरफ्लो के झंडे या शेयर संवादों के साथ या अपने ब्राउज़र के शीर्ष मेनू बार से एक ड्रॉपडाउन के साथ इसे आज़माएं ।
मार्क एमी

ReactDOM.findDOMNodeऔर स्ट्रिंग refको हटा दिया गया है, refकॉलबैक का उपयोग करना चाहिए : github.com/yannickcr/eslint-plugin-react/issues/…
dain

यह सबसे सरल उपाय है और मेरे लिए भी पूरी तरह से काम करता हैdocument
फ्लॉयन

37

JSConf हवाई 2020 में टान्नर लिंसले की उत्कृष्ट बातचीत पर आधारित हुक कार्यान्वयन :

useOuterClick हुक एपीआई

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के लिए एक दुबला एपीआई बनाने के लिए परिवर्तनशील रेफ का उपयोग करता है Client। इसके माध्यम से याद करने के लिए बिना कॉलबैक सेट किया जा सकता है useCallback। कॉलबैक बॉडी की पहुंच अभी भी सबसे हालिया प्रॉप्स और स्टेट (नो स्टेल क्लोजर वैल्यू) तक है।


IOS उपयोगकर्ताओं के लिए ध्यान दें

iOS केवल कुछ तत्वों को क्लिक करने योग्य मानता है। इस व्यवहार को दरकिनार करने के लिए, एक अलग बाहरी क्लिक श्रोता चुनें document- जिसमें ऊपर की ओर कुछ भी नहीं है body। उदाहरण के लिए, आप divऊपर दिए गए उदाहरण में रिएक्ट रूट पर एक श्रोता जोड़ सकते हैं और height: 100vhसभी बाहरी क्लिकों को पकड़ने के लिए इसकी ऊंचाई ( या समान) का विस्तार कर सकते हैं। स्रोत: quirksmode.org और यह उत्तर


मोबाइल पर काम इतना अप्रत्याशित नहीं है
उमर

@ उमर ने सिर्फ एंड्रॉइड डिवाइस पर फ़ायरफ़ॉक्स 67.0.3 के साथ मेरी पोस्ट में कोड्सबॉक्स का परीक्षण किया - सभी ठीक काम करता है। क्या आप विस्तृत कर सकते हैं कि आप किस ब्राउज़र का उपयोग करते हैं, त्रुटि क्या है आदि?
ford04

कोई त्रुटि नहीं है, लेकिन क्रोम पर काम नहीं करते, iPhone 7+। यह बाहर नल का पता नहीं लगाता है। मोबाइल पर क्रोम देव टूल्स में यह काम करता है लेकिन असली आईओएस डिवाइस में यह काम नहीं करता है।
उमर

1
@ ford04 कि बाहर खुदाई के लिए धन्यवाद, मैं एक और धागा है कि निम्नलिखित चाल का सुझाव दे रहा था पर ठोकर खाई । @media (hover: none) and (pointer: coarse) { body { cursor:pointer } }मेरी वैश्विक शैलियों में इसे शामिल करने से लगता है कि इसने समस्या को ठीक कर दिया है।
कोस्तास सेरनोपुलोस 15

1
@ ford04 हाँ, इस धागे के अनुसार btw वहाँ एक और भी अधिक विशिष्ट मीडिया क्वेरी है @supports (-webkit-overflow-scrolling: touch)जो केवल IOS को लक्षित करता है, भले ही मुझे अधिक पता न हो! मैंने अभी इसका परीक्षण किया है और यह काम करता है।
कोस्टा सेरेंटोपोलोस

30

यहां किसी भी अन्य उत्तर ने मेरे लिए काम नहीं किया। मैं कलंक पर एक पॉपअप को छिपाने की कोशिश कर रहा था, लेकिन चूंकि सामग्री पूरी तरह से तैनात थी, इसलिए आंतरिक सामग्री के क्लिक पर भी ऑन फायर किया जा रहा था।

यहाँ एक दृष्टिकोण है कि मेरे लिए काम किया है:

// 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.
    }
},

उम्मीद है की यह मदद करेगा।


बहुत अच्छा! धन्यवाद। लेकिन मेरे मामले में यह div के लिए पूर्ण स्थिति के साथ "वर्तमान स्थान" को पकड़ने के लिए एक समस्या थी, इसलिए मैं इनपुट के साथ div के लिए "OnMouseLeave" का उपयोग करता हूं और कैलेंडर को छोड़ देता हूं जब माउस छोड़ने पर सभी div को अक्षम कर देता है।
एलेक्स

1
धन्यवाद! मेरी बहुत मदद करो।
31ngelo Lucas

1
मुझे यह उन तरीकों से बेहतर लगता है जो संलग्न हैं document। धन्यवाद।
किव

यह काम करने के लिए आपको यह सुनिश्चित करने की आवश्यकता है कि क्लिक किया गया तत्व (संबंधितटारगेट) ध्यान देने योग्य है। देखें stackoverflow.com/questions/42764494/…
xabitrigo

21

[अद्यतन] प्रतिक्रिया के साथ समाधान ^ १६. using हुक का उपयोग कर

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;

प्रतिक्रिया के साथ समाधान ^ 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;

3
यह अब जाने वाला समाधान है। यदि आपको त्रुटि मिल रही है .contains is not a function, तो ऐसा हो सकता है क्योंकि आप एक वास्तविक DOM तत्व की बजाय एक कस्टम कंपोनेंट के लिए रेफ प्रोप को पास कर रहे हैं जैसे<div>
willlma

पारित करने के लिए कोशिश कर रहा है उन लोगों के लिए refएक कस्टम घटक को प्रोप, आप पर एक नजर है कर सकते हैंReact.forwardRef
onoya

4

यहाँ मेरा दृष्टिकोण है (डेमो - 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>
    );
  }
}

महान समाधान - मेरे उपयोग के लिए, मैंने छोटी सामग्री वाले पृष्ठों के मामले में शरीर के बजाय दस्तावेज़ को सुनने के लिए बदल दिया, और स्ट्रिंग से डोम संदर्भ में बदल दिया: jsfiddle.net/agymay93/9
बिली मून

और div के बजाय स्पैन का उपयोग किया जाता है क्योंकि इसमें लेआउट बदलने की संभावना कम होती है, और शरीर के बाहर क्लिकिंग के
बिली मून

स्ट्रिंग refपदावनत है, refकॉलबैक का उपयोग करना चाहिए : reactjs.org/docs/refs-and-the-dom.html
dain

4

इसके पास पहले से ही कई उत्तर हैं, लेकिन वे उस पते को संबोधित नहीं करते हैं e.stopPropagation()और जिस तत्व को आप बंद करना चाहते हैं उसके बाहर प्रतिक्रिया लिंक पर क्लिक करने से रोकते हैं।

इस तथ्य के कारण कि रिएक्ट का अपना कृत्रिम ईवेंट हैंडलर है, आप इवेंट श्रोताओं के लिए आधार के रूप में दस्तावेज़ का उपयोग करने में सक्षम नहीं हैं। आपको e.stopPropagation()इससे पहले की आवश्यकता है क्योंकि रिएक्ट स्वयं दस्तावेज़ का उपयोग करता है। यदि आप document.querySelector('body')इसके बजाय उदाहरण के लिए उपयोग करते हैं । आप प्रतिक्रिया लिंक से क्लिक को रोकने में सक्षम हैं। निम्नलिखित इस बात का उदाहरण है कि मैं कैसे लागू करता हूं बाहर और पास पर क्लिक करें।
यह ईएस 6 और रिएक्ट 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;

3

जिन लोगों को पूर्ण स्थिति की आवश्यकता होती है, उनके लिए एक सरल विकल्प जो मैंने चुना है वह है एक रैपर घटक जोड़ना जो पूरे पृष्ठ को पारदर्शी पृष्ठभूमि के साथ कवर करने के लिए स्टाइल किया गया हो। फिर आप अपने अंदर के घटक को बंद करने के लिए इस तत्व पर एक ऑनक्लिक जोड़ सकते हैं।

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

जैसा कि अभी है, यदि आप सामग्री पर एक क्लिक हैंडलर जोड़ते हैं, तो ईवेंट को ऊपरी div पर भी प्रचारित किया जाएगा और इसलिए हैंडलर को क्लिक करें। यदि यह आपका वांछित व्यवहार नहीं है, तो बस अपने हैंडलर पर घटना के प्रचार को रोक दें।

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

`


इस दृष्टिकोण के साथ मुद्दा यह है कि आपके पास सामग्री पर कोई क्लिक नहीं हो सकता है - div को इसके बजाय क्लिक प्राप्त होगा।

चूंकि सामग्री div में है यदि आप ईवेंट प्रचार को रोकते नहीं हैं तो दोनों को क्लिक प्राप्त होगा। यह अक्सर एक वांछित व्यवहार होता है, लेकिन यदि आप नहीं चाहते हैं कि क्लिक को div को प्रचारित किया जाए, तो बस अपने हैंडलर ऑन कंटेंट पर EventPropagation को रोकें। मैंने अपने उत्तर को अपडेट किया है कि कैसे दिखाया जाए।
एंथनी गारंट

3

बेन बड द्वारा किए गए स्वीकृत उत्तर का विस्तार करने के लिए , यदि आप स्टाइल-घटकों का उपयोग कर रहे हैं, तो उस तरह से गुजरने से आपको "this.wrapperRef.contains एक फ़ंक्शन नहीं है" जैसी त्रुटि मिलेगी।

टिप्पणियों में सुझाए गए फिक्स, स्टाइल वाले घटक को एक डिव के साथ लपेटने और वहां रेफरी पास करने के लिए, काम करता है। कहा जाता है कि, अपने डॉक्स में वे पहले से ही इसका कारण बताते हैं और स्टाइल-घटकों के भीतर रिफ का उचित उपयोग करते हैं:

स्टाइल कंपोनेंट के लिए रिफ प्रॉप पास करना आपको स्टाइलडीकंपोनेंट रैपर का एक उदाहरण देगा, लेकिन अंतर्निहित डोम नोड को नहीं। यह इस वजह से है कि रेफरी कैसे काम करता है। हमारे रैपर्स पर सीधे फोकस जैसे डोम तरीकों को कॉल करना संभव नहीं है। वास्तविक, लिपटे डोम नोड के लिए एक रेफरी प्राप्त करने के लिए, कॉलबैक को इनर रीफ प्रोप के बजाय पास करें।

इस तरह:

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


2

अन्य सभी उत्तरों के साथ मेरी सबसे बड़ी चिंता यह है कि क्लिक इवेंट्स को रूट / पैरेंट डाउन से फ़िल्टर करें। मुझे सबसे आसान तरीका यह लगा कि बस सिबलिंग एलीमेंट को पोज़िशन के साथ सेट करना था: फिक्स्ड, ड्रॉपडाउन के पीछे एक 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;
    }
}

1
हम्म निफ्टी। क्या यह कई उदाहरणों के साथ काम करेगा? या प्रत्येक निश्चित तत्व संभावित रूप से दूसरों के लिए क्लिक को अवरुद्ध करेगा?
थाइज कोसरेलमैन

2
componentWillMount(){

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

handleClickOutside(event) {

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

ईवेंट path[0]अंतिम आइटम पर क्लिक किया गया है


3
सुनिश्चित करें कि आप इवेंट श्रोता को हटा दें जब घटक स्मृति प्रबंधन के मुद्दों, आदि को रोकने के लिए
अनमाउंट करता है

2

मैंने इस मॉड्यूल का उपयोग किया (लेखक से मेरा कोई संबंध नहीं है)

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

इसने अच्छा काम किया।


यह काल्पनिक रूप से काम करता है! कई अन्य उत्तरों की तुलना में बहुत सरल और यहाँ कम से कम 3 अन्य समाधानों के विपरीत, जहाँ उन सभी के लिए मैंने इसे पुनः प्राप्त "Support for the experimental syntax 'classProperties' isn't currently enabled"किया बस सीधे बॉक्स से बाहर काम किया। इस समाधान की कोशिश करने वाले किसी भी व्यक्ति के लिए, किसी अन्य समाधान की कोशिश करते समय आपके द्वारा कॉपी किए गए किसी भी कोड को मिटाना याद रखें। उदाहरण के लिए ईवेंट श्रोताओं को जोड़ना इसके साथ संघर्ष करता है।
फ्राइस्टर

2

मैं आंशिक रूप से पालन करते हुए ऐसा किया इस और निम्न refs से निपटने पर सरकारी डॉक्स जो प्रतिक्रिया ^ 16.3 आवश्यकता प्रतिक्रिया से। यह केवल एक चीज है जो मेरे लिए यहां कुछ अन्य सुझावों की कोशिश करने के बाद काम की है ...

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

1

रणनीति के साथ एक उदाहरण

मुझे प्रदान किए गए समाधान पसंद हैं जो घटक के चारों ओर एक आवरण बनाकर एक ही काम करने के लिए उपयोग करते हैं।

चूंकि यह एक व्यवहार के बारे में अधिक है जो मैंने रणनीति के बारे में सोचा था और निम्नलिखित के साथ आया था।

मैं रिएक्ट के साथ नया हूं और उपयोग के मामलों में कुछ बॉयलरप्लेट को बचाने के लिए मुझे थोड़ी मदद की जरूरत है

कृपया समीक्षा करें और मुझे बताएं कि आप क्या सोचते हैं।

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
यह इस व्यवहार के उपयोगकर्ता से सक्षम / अक्षम बॉयलरप्लेट को हटा देगा लेकिन यह बहुत अच्छा नहीं लगता है


1

मेरे ड्रॉपडाउन मामले में बेन बड के समाधान ने अच्छी तरह से काम किया, लेकिन मेरे पास एक ऑनक्लिक हैंडलर के साथ एक अलग टॉगल बटन था। तो बाहर क्लिक करने वाले तर्क को बटन पर क्लिक करके टॉगल करें। यहां बताया गया है कि कैसे मैंने बटन के रेफ को पास करके इसे हल किया:

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

0

आप एक छोटे घटक उपयोग करना चाहते हैं (466 बाइट gzipped) कि पहले से ही इस कार्यक्षमता के लिए मौजूद है तो आप इस पुस्तकालय की जांच कर सकते प्रतिक्रिया-outclick

पुस्तकालय के बारे में अच्छी बात यह है कि यह आपको एक घटक के बाहर और दूसरे के अंदर क्लिकों का पता लगाने की सुविधा भी देता है। यह अन्य प्रकार की घटनाओं का पता लगाने का भी समर्थन करता है।


0

आप एक छोटे घटक उपयोग करना चाहते हैं (466 बाइट gzipped) कि पहले से ही इस कार्यक्षमता के लिए मौजूद है तो आप इस पुस्तकालय की जांच कर सकते प्रतिक्रिया-outclick । यह एक प्रतिक्रिया घटक या jsx तत्व के बाहर की घटनाओं को कैप्चर करता है।

पुस्तकालय के बारे में अच्छी बात यह है कि यह आपको एक घटक के बाहर और दूसरे के अंदर क्लिकों का पता लगाने की सुविधा भी देता है। यह अन्य प्रकार की घटनाओं का पता लगाने का भी समर्थन करता है।


0

मुझे पता है कि यह एक पुराना सवाल है, लेकिन मैं इस पर ध्यान देता हूं और मुझे इसे एक सरल प्रारूप में समझने में बहुत परेशानी हुई। तो अगर यह किसी के जीवन को थोड़ा आसान बना देगा, तो 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>
 )
}

0
import { useClickAway } from "react-use";

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

0

आप अपनी समस्या को हल करने का एक सरल तरीका बता सकते हैं, मैं आपको दिखाता हूं:

....

const [dropDwonStatus , setDropDownStatus] = useState(false)

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

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

यह मेरे लिए काम करता है, सौभाग्य;)


-1

मुझे यह लेख नीचे से मिला:

रेंडर () {रिटर्न ({this.node = नोड;}}} टॉगल पॉपओवर {this.state.popupVouble && (मैं एक पॉपओवर!)}); }}

यहाँ इस मुद्दे के बारे में एक शानदार लेख है: "प्रतिक्रिया घटकों के बाहर हैंडल क्लिक करें " https://larsgraubner.com/handle-outside-clicks-react/


ढेर अतिप्रवाह में आपका स्वागत है! उत्तर जो केवल एक कड़ी हैं, आमतौर पर इस पर आधारित होते हैं: meta.stackoverflow.com/questions/251006/… । भविष्य में, प्रश्न के लिए प्रासंगिक जानकारी के साथ एक उद्धरण या कोड उदाहरण शामिल करें। चीयर्स!
फ्रेड स्टार्क

-1

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

निश्चित नहीं है कि कौन अपमानजनक है लेकिन बाध्यकारी श्रोताओं / बंधनकारी श्रोताओं की तुलना में यह समाधान बहुत साफ है। मेरे पास इस कार्यान्वयन के साथ शून्य मुद्दे हैं।
wdm

-1

बनाने के लिए 'फोकस' घटना श्रोताओं के साथ लटकती के लिए समाधान काम आप उनके साथ जोड़ सकते हैं onMouseDown के बजाय घटना onClick । इस तरह से घटना में आग लग जाएगी और उसके बाद पॉपअप बंद हो जाएगा:

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

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

और import ReactDOM from 'react-dom';
डिपोले_मोमेंट

-1

मैंने सभी अवसरों के लिए एक समाधान बनाया।

आपको उस घटक को लपेटने के लिए एक उच्च आदेश घटक का उपयोग करना चाहिए जिसे आप इसके बाहर क्लिक के लिए सुनना चाहते हैं।

इस घटक उदाहरण में केवल एक प्रोप है: "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>;
  }
}

-1

UseOnClickOutside हुक - प्रतिक्रिया 16.8 +

एक सामान्य उपयोगऑनओटसाइडक्लिक फ़ंक्शन बनाएँ

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 उत्तर से प्रेरित है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.