Chcę zamknąć menu rozwijane, gdy nastąpi kliknięcie poza elementem rozwijanym.
Jak mogę to zrobić?
Chcę zamknąć menu rozwijane, gdy nastąpi kliknięcie poza elementem rozwijanym.
Jak mogę to zrobić?
Odpowiedzi:
W elemencie dodałem mousedowni mouseuptak:
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 showCalto wartość logiczna, która kiedy truepokazuje w moim przypadku kalendarz i falseukrywa 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
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/
this.refs.componentodnosi 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łącz onBlurdo 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 tabklucz 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>
);
}
});
onBlurdiv działa również z div, jeśli ma tabIndexatrybut
Napisałem ogólny program obsługi zdarzeń dla zdarzeń, które pochodzą spoza komponentu, reakcja poza zdarzeniem .
Sama implementacja jest prosta:
windowobiektu dołączana jest procedura obsługi zdarzeń .onOutsideEventkomponent 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']);
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.
.backdrop)..target) poza .backdropelementem i z większym indeksem stosu ( z-index).Wtedy każde kliknięcie na .backdropelement będzie traktowane jako „poza .targetelementem”.
.click-overlay {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
z-index: 1;
}
.target {
position: relative;
z-index: 2;
}
Możesz użyć refs, aby to osiągnąć, coś takiego powinno działać.
Dodaj refdo 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
}
handleClickOutsidew document.addEventListener()dodając thisodniesienie. 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.
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.
Skorzystaj z doskonałej mieszanki reakcyjnej na zewnątrz :
npm install --save react-onclickoutside
I wtedy
var Component = React.createClass({
mixins: [
require('react-onclickoutside')
],
handleClickOutside: function(evt) {
// ...handling code goes here...
}
});