Chcę zamknąć menu rozwijane, gdy nastąpi kliknięcie poza elementem rozwijanym.
Jak mogę to zrobić?
W elemencie dodałem mousedown
i mouseup
tak:
onMouseDown={this.props.onMouseDown} onMouseUp={this.props.onMouseUp}
Następnie u rodzica robię to:
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;
}
Jest showCal
to wartość logiczna, która kiedy true
pokazuje w moim przypadku kalendarz i false
ukrywa go.
preventDefault()
z Androidem , ponieważ musisz natychmiast wywoływać zdarzenia lub włącza się natywne zachowanie Androida, z którym przeszkadza przetwarzanie wstępne Reacta. Od tego czasu napisałem npmjs.com/package/react-onclickoutside .Korzystając z metod cyklu życia, dodaj i usuń detektory zdarzeń w dokumencie.
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); } });
Sprawdź wiersze 48-54 tego komponentu: https://github.com/i-like-robots/react-tube-tracker/blob/91dc0129a1f6077bef57ea4ad9a860be0c600e9d/app/component/tube-tracker.jsx#L48-54
źródło
Spójrz na cel zdarzenia, jeśli zdarzenie było bezpośrednio w komponencie lub elementach podrzędnych tego komponentu, to kliknięcie było wewnątrz. W przeciwnym razie był na zewnątrz.
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> ) } });
Jeśli ma być zastosowany w wielu komponentach, to ładniej jest z mieszanką:
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); }, }
Zobacz przykład tutaj: https://jsfiddle.net/0Lshs7mg/1/
źródło
this.refs.component
odnosi się do elementu DOM z 0.14 facebook.github.io/react/blog/2015/07/03/…Dla twojego konkretnego przypadku użycia, obecnie akceptowana odpowiedź jest odrobinę przesadzona. Jeśli chcesz nasłuchiwać, kiedy użytkownik opuszcza listę rozwijaną, po prostu użyj
<select>
komponentu jako elementu nadrzędnego i dołączonBlur
do niego procedurę obsługi.Jedyną wadą tego podejścia jest to, że zakłada ono, że użytkownik zachował już fokus na elemencie i opiera się na kontrolce formularza (która może być tym, czego chcesz, ale nie musi, jeśli weźmiesz pod uwagę, że
tab
klucz również skupia się i rozmywa elementy ) - ale te wady są tak naprawdę ograniczeniem tylko dla bardziej skomplikowanych przypadków użycia, w którym to przypadku może być konieczne bardziej skomplikowane rozwiązanie.var Dropdown = React.createClass({ handleBlur: function(e) { // do something when user clicks outside of this element }, render: function() { return ( <select onBlur={this.handleBlur}> ... </select> ); } });
źródło
onBlur
div działa również z div, jeśli matabIndex
atrybutNapisałem ogólny program obsługi zdarzeń dla zdarzeń, które pochodzą spoza komponentu, reakcja poza zdarzeniem .
Sama implementacja jest prosta:
window
obiektu dołączana jest procedura obsługi zdarzeń .onOutsideEvent
komponent docelowy.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} />; } } };
Aby użyć komponentu, musisz zawinąć deklarację klasy komponentu docelowego za pomocą komponentu wyższego rzędu i zdefiniować zdarzenia, które chcesz obsłużyć:
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']);
źródło
Głosowałem na jedną z odpowiedzi, mimo że nie zadziałała. To doprowadziło mnie do tego rozwiązania. Nieznacznie zmieniłem kolejność operacji. Nasłuchuję mouseDown na celu i mouseUp na celu. Jeśli którykolwiek z nich zwraca TRUE, nie zamykamy modalu. Gdy tylko kliknięcie zostanie zarejestrowane, w dowolnym miejscu te dwa parametry logiczne {mouseDownOnModal, mouseUpOnModal} są ustawiane z powrotem na 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> ); }
Ma to tę zaletę, że cały kod spoczywa na komponencie potomnym, a nie nadrzędnym. Oznacza to, że nie ma standardowego kodu do skopiowania podczas ponownego użycia tego komponentu.
źródło
.backdrop
)..target
) poza.backdrop
elementem i z większym indeksem stosu (z-index
).Wtedy każde kliknięcie na
.backdrop
element będzie traktowane jako „poza.target
elementem”..click-overlay { position: fixed; left: 0; right: 0; top: 0; bottom: 0; z-index: 1; } .target { position: relative; z-index: 2; }
źródło
Możesz użyć
ref
s, aby to osiągnąć, coś takiego powinno działać.Dodaj
ref
do swojego elementu:<div ref={(element) => { this.myElement = element; }}></div>
Następnie możesz dodać funkcję do obsługi kliknięcia poza elementem w następujący sposób:
handleClickOutside(e) { if (!this.myElement.contains(e)) { this.setState({ myElementVisibility: false }); } }
Na koniec dodaj i usuń nasłuchiwania zdarzeń, które zostaną zamontowane i odmontowane.
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 }
źródło
handleClickOutside
wdocument.addEventListener()
dodającthis
odniesienie. W przeciwnym razie zwraca błąd Uncaught ReferenceError: handleClickOutside nie jest zdefiniowany wcomponentWillMount()
Bardzo późno na imprezę, ale udało mi się ustawić zdarzenie rozmycia w elemencie nadrzędnym listy rozwijanej z powiązanym kodem, aby zamknąć listę rozwijaną, a także dołączyć nasłuchiwanie myszy do elementu nadrzędnego, który sprawdza, czy lista rozwijana jest otwarta lub nie, i zatrzyma propagację zdarzenia, jeśli jest otwarte, aby zdarzenie rozmycia nie zostało uruchomione.
Ponieważ zdarzenie przesunięcia kursora się pojawi, zapobiegnie to zamazaniu myszy u dzieci.
/* 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 () nie powinno być wywoływane tak daleko, jak potrafię, ale Firefox z jakiegoś powodu nie działa dobrze bez niego. Działa w Chrome, Firefox i Safari.
źródło
Znalazłem prostszy sposób.
Musisz tylko dodać
onHide(this.closeFunction)
modal<Modal onHide={this.closeFunction}> ... </Modal>
Zakładając, że masz funkcję zamykania modalu.
źródło
Skorzystaj z doskonałej mieszanki reakcyjnej na zewnątrz :
I wtedy
var Component = React.createClass({ mixins: [ require('react-onclickoutside') ], handleClickOutside: function(evt) { // ...handling code goes here... } });
źródło