एक घटक के बाहर होने वाली क्लिक घटनाओं को कैसे सुनें


89

मैं एक ड्रॉपडाउन मेनू को बंद करना चाहता हूं जब ड्रॉपडाउन घटक के बाहर एक क्लिक होता है।

मैं उसको कैसे करू?

जवाबों:


59

तत्व में मैंने इसे जोड़ा है mousedownऔर mouseupइस तरह है:

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यह खाल।


यह विशेष रूप से माउस पर क्लिक को जोड़ता है, हालांकि। टच ईवेंट द्वारा क्लिक की गई घटनाओं को उत्पन्न किया जा सकता है और चाबियाँ भी दर्ज की जा सकती हैं, जिस पर यह समाधान प्रतिक्रिया नहीं कर पाएगा, जिससे यह मोबाइल और पहुंच उद्देश्यों के लिए अनुपयुक्त हो जाएगा = (
माइक 'पोमैक्स' कमरमन्स

@ माइक'पोमैक्स’कैमरेन्स अब आप मोबाइल के लिए onTouchStart और onTouchEnd का उपयोग कर सकते हैं। facebook.github.io/react/docs/events.html#touch-events
'16

3
वे लंबे समय से अस्तित्व में हैं, लेकिन एंड्रॉइड के साथ अच्छा नहीं खेलेंगे, क्योंकि आपको preventDefault()तुरंत घटनाओं पर कॉल करने की आवश्यकता है या देशी एंड्रॉइड व्यवहार में किक करता है, जिसके साथ रिएक्ट का प्रीप्रोसेसिंग हस्तक्षेप करता है। मैंने npmjs.com/package/react-onclickoutside को लिखा है।
माइक 'पोमैक्स' कमेरमन्स

मुझे यह पसंद है! की सराहना की। मूसडाउन पर ईवेंट श्रोता को हटाना सहायक होगा। ComponentsWillUnmount = () => window.removeEventListener ('मूसडाउन', this.pageClick, false);
जूनी ब्रोसस

73

जीवन-चक्र के तरीकों का उपयोग करते हुए दस्तावेज़ में घटना श्रोताओं को जोड़ते हैं और हटाते हैं।

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/91dc0129a1f6077bef57ea4ad9a860be0cbc9d/app/component/tube-tracker.jsx#L48-5448-54


यह दस्तावेज़ में एक जोड़ता है, लेकिन इसका मतलब है कि घटक पर कोई भी क्लिक करने की घटना भी दस्तावेज़ घटना को ट्रिगर करती है। यह स्वयं के मुद्दों का कारण बनता है क्योंकि दस्तावेज़ श्रोता का लक्ष्य हमेशा घटक के अंत में कुछ कम डाउन डिव होता है, घटक ही नहीं।
एलन हॉर्टले

मुझे यकीन नहीं है कि मैं आपकी टिप्पणी का पालन कर रहा हूं, लेकिन अगर आप घटना के श्रोताओं को दस्तावेज़ से बांधते हैं, तो आप उस पर क्लिक करने वाली किसी भी घटना को फ़िल्टर कर सकते हैं, या तो चयनकर्ता (मानक ईवेंट प्रतिनिधिमंडल) या किसी अन्य मध्यस्थ आवश्यकताओं (ईजी से मेल करके) लक्ष्य तत्व दूसरे तत्व के भीतर नहीं )।
I_like_robots

3
यह @AllanHortle के रूप में समस्याओं का कारण बनता है। एक प्रतिक्रिया घटना पर stopPropagation दस्तावेज़ घटना संचालकों को घटना प्राप्त करने से नहीं रोक पाएगा।
मैट क्रिंकलाव-वोग्ट

4
किसी भी इच्छुक के लिए मुझे दस्तावेज़ का उपयोग करते समय stopPropagation के साथ समस्याएँ थीं। यदि आप अपने ईवेंट श्रोता को विंडो में संलग्न करते हैं तो यह इस समस्या को ठीक करता है?
टाइटस

10
जैसा कि इस बात का उल्लेख किया गया है ।getDOMNode () अप्रचलित है। इसके बजाय ReactDOM का उपयोग करें: ReactDOM.findDOMNode (यह) .contains (
e.target

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

यदि इसे कई घटकों में उपयोग किया जाना है, तो यह एक मिश्रण के साथ अच्छा है:

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/


@ माइक'पोमैक्स’कैमरेन्स जो तय हो गए हैं, मुझे लगता है कि यह उत्तर उपयोगी जानकारी जोड़ता है, शायद आपकी टिप्पणी अब हटा दी जाए।
जा

आपने गलत कारणों से मेरे परिवर्तन वापस कर दिए हैं। this.refs.componentDOM तत्व का जिक्र 0.14 facebook.github.io/react/blog/2015/07/03/… के
Gajus

@ GajusKuizinas - 0.14 में एक बार परिवर्तन करने के लिए ठीक है नवीनतम रिलीज़ (अब बीटा है)।
ja

डॉलर क्या है?
प्रोनबर्ड

2
मैं प्रतिक्रिया के साथ jQuery को प्रहार करने से नफरत करता हूं क्योंकि वे दो दृश्य के ढांचे हैं।
अब्देनौर TOUMI

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

10
onBlurdiv भी div के साथ काम करता है अगर इसमें tabIndexगुण है
gorpacrate

मेरे लिए यह वह उपाय था जो मुझे सबसे सरल और आसान लगा, मैं इसे एक बटन तत्व पर उपयोग कर रहा हूं और एक आकर्षण की तरह काम करता हूं। धन्यवाद!
अमीर 5000

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

घटक का उपयोग करने के लिए, आपको उच्च आदेश घटक का उपयोग करके लक्ष्य घटक वर्ग घोषणा को लपेटने की आवश्यकता है और उन घटनाओं को परिभाषित करें जिन्हें आप संभालना चाहते हैं:

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

4

भले ही यह मेरे लिए काम न करे, मैंने एक जवाब दिया। इसने मुझे इस समाधान की ओर अग्रसर किया। मैंने संचालन के क्रम को थोड़ा बदल दिया। मैं लक्ष्य पर माउसडाउन और लक्ष्य पर माउसअप के लिए सुनता हूं। यदि उनमें से कोई भी TRUE लौटाता है, तो हम मोडल को बंद नहीं करते हैं। जैसे ही एक क्लिक पंजीकृत होता है, कहीं भी, उन दो बूलियन {mouseDownOnModal, mouseUpOnModal} को गलत पर सेट किया जाता है।

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

इसका यह फायदा है कि सभी कोड चाइल्ड कंपोनेंट के साथ रहते हैं, न कि पेरेंट्स के। इसका मतलब है कि इस घटक का पुन: उपयोग करते समय कॉपी करने के लिए कोई बॉयलरप्लेट कोड नहीं है।


3
  1. एक निश्चित परत बनाएं जो पूरी स्क्रीन को फैलाती है (.backdrop )।
  2. तत्व के .targetबाहर .backdropऔर अधिक स्टैकिंग इंडेक्स ( z-index) के साथ लक्ष्य तत्व ( ) रखें।

तब .backdropतत्व पर किसी भी क्लिक को " .targetतत्व के बाहर" माना जाएगा ।

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

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

2

आप refइसे प्राप्त करने के लिए s का उपयोग कर सकते हैं , निम्नलिखित की तरह कुछ काम करना चाहिए।

refअपने तत्व में जोड़ें :

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

आप तत्पश्चात तत्व के बाहर क्लिक से निपटने के लिए एक फ़ंक्शन जोड़ सकते हैं:

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

फिर अंत में, घटना श्रोताओं को जोड़ने और हटाने पर माउंट हो जाएगा और अनमाउंट हो जाएगा।

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
}

यदि आपका जवाब संशोधित, यह बुला के संदर्भ में संभव त्रुटि थी handleClickOutsideमें document.addEventListener()जोड़कर thisसंदर्भ। अन्यथा यह देता है Uncaught ReferenceError: handleClickOutside परिभाषित नहीं है मेंcomponentWillMount()
मुहम्मद हन्नान

1

पार्टी के लिए सुपर देर से, लेकिन मुझे ड्रॉपडाउन के मूल तत्व पर एक धब्बा घटना सेट करने में सफलता मिली है और ड्रॉपडाउन को बंद करने के लिए संबद्ध कोड के साथ एक मूसडाउन श्रोता को संलग्न करना है जो यह जांचता है कि ड्रॉपडाउन खुला है या नहीं या नहीं, और अगर यह खुला है कि घटना के प्रसार को रोक देगा ताकि धब्बा घटना ट्रिगर नहीं होगा।

चूंकि मूसडाउन इवेंट में बुलबुले आते हैं, इसलिए यह बच्चों पर किसी भी मूसडाउन को माता-पिता पर धब्बा पैदा करने से रोकेगा।

/* 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 () को जहां तक ​​कारण हो सकता है, तब तक नहीं बुलाया जाना चाहिए, लेकिन फायरफॉक्स इसके बिना अच्छा नहीं लगता है। क्रोम, फ़ायरफ़ॉक्स और सफारी पर काम करता है।


0

मुझे इस बारे में एक सरल तरीका मिला।

आपको बस onHide(this.closeFunction)मोडल पर जोड़ना होगा

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

मान लें कि आपके पास मोडल को बंद करने के लिए एक फ़ंक्शन है।


-1

उत्कृष्ट प्रतिक्रिया-ऑनक्लिकआउटसाइड मिक्सिन का उपयोग करें :

npm install --save react-onclickoutside

और तब

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

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