Ich möchte ein Dropdown-Menü schließen, wenn ein Klick außerhalb der Dropdown-Komponente auftritt.
Wie mache ich das?
Ich möchte ein Dropdown-Menü schließen, wenn ein Klick außerhalb der Dropdown-Komponente auftritt.
Wie mache ich das?
Antworten:
In dem Element, das ich hinzugefügt habe mousedownund das mouseupgefällt:
onMouseDown={this.props.onMouseDown} onMouseUp={this.props.onMouseUp}
Dann mache ich beim Elternteil Folgendes:
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;
}
Das showCalist ein Boolescher Wert, der truein meinem Fall einen Kalender anzeigt und falseversteckt.
preventDefault()die Ereignisse sofort aufrufen müssen oder das native Android-Verhalten einsetzt, das durch die Vorverarbeitung von React beeinträchtigt wird. Ich habe seitdem npmjs.com/package/react-onclickoutside geschrieben .
Mithilfe der Lebenszyklusmethoden können Sie dem Dokument Ereignis-Listener hinzufügen und daraus entfernen.
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);
}
});
Überprüfen Sie die Zeilen 48-54 dieser Komponente: https://github.com/i-like-robots/react-tube-tracker/blob/91dc0129a1f6077bef57ea4ad9a860be0c600e9d/app/component/tube-tracker.jsx#L48-54
Sehen Sie sich das Ziel des Ereignisses an. Wenn sich das Ereignis direkt auf der Komponente oder untergeordneten Elementen dieser Komponente befand, befand sich der Klick im Inneren. Ansonsten war es draußen.
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>
)
}
});
Wenn dies in vielen Komponenten verwendet werden soll, ist es mit einem Mixin schöner:
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);
},
}
Siehe Beispiel hier: https://jsfiddle.net/0Lshs7mg/1/
this.refs.componentbezieht sich auf das DOM-Element ab
Für Ihren speziellen Anwendungsfall ist die derzeit akzeptierte Antwort etwas überarbeitet. Wenn Sie warten möchten, wenn ein Benutzer aus einer Dropdown-Liste klickt, verwenden Sie einfach eine <select>Komponente als übergeordnetes Element und fügen Sie eine hinzuonBlur Handler hinzu.
Der einzige Nachteil dieses Ansatzes besteht darin, dass davon ausgegangen wird, dass der Benutzer den Fokus auf das Element bereits beibehalten hat, und dass er sich auf ein Formularsteuerelement stützt (das möglicherweise das ist, was Sie möchten oder nicht, wenn Sie berücksichtigen, dass der tabSchlüssel auch Elemente fokussiert und verwischt ) - aber diese Nachteile sind nur eine Grenze für kompliziertere Anwendungsfälle. In diesem Fall könnte eine kompliziertere Lösung erforderlich sein.
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 funktioniert auch mit div, wenn es tabIndexAttribut hat
Ich habe einen generischen Ereignishandler für Ereignisse geschrieben, die außerhalb der Komponente " Reagieren außerhalb des Ereignisses" entstehen .
Die Implementierung selbst ist einfach:
windowObjekt angehängt .onOutsideEventdie Zielkomponente ausgelöst.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} />;
}
}
};
Um die Komponente zu verwenden, müssen Sie die Deklaration der Zielkomponentenklasse mit der Komponente höherer Ordnung umbrechen und die Ereignisse definieren, die Sie behandeln möchten:
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']);
Ich habe eine der Antworten gewählt, obwohl es bei mir nicht funktioniert hat. Es führte mich schließlich zu dieser Lösung. Ich habe die Reihenfolge der Operationen leicht geändert. Ich höre auf mouseDown auf dem Ziel und mouseUp auf dem Ziel. Wenn einer von beiden TRUE zurückgibt, schließen wir das Modal nicht. Sobald ein Klick registriert ist, werden diese beiden Booleschen Werte {mouseDownOnModal, mouseUpOnModal} auf false zurückgesetzt.
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>
);
}
Dies hat den Vorteil, dass der gesamte Code bei der untergeordneten Komponente und nicht bei der übergeordneten Komponente liegt. Dies bedeutet, dass bei der Wiederverwendung dieser Komponente kein Code für das Boilerplate kopiert werden muss.
.backdrop )..target) außerhalb des .backdropElements und mit einem größeren Stapelindex ( z-index).Dann wird jeder Klick auf das .backdropElement als "außerhalb des .targetElements" betrachtet.
.click-overlay {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
z-index: 1;
}
.target {
position: relative;
z-index: 2;
}
Du könntest benutzen ref s verwenden, um dies zu erreichen. So etwas wie das Folgende sollte funktionieren.
Fügen Sie das refzu Ihrem Element hinzu:
<div ref={(element) => { this.myElement = element; }}></div>
Sie können dann eine Funktion zum Behandeln des Klicks außerhalb des Elements wie folgt hinzufügen:
handleClickOutside(e) {
if (!this.myElement.contains(e)) {
this.setState({ myElementVisibility: false });
}
}
Fügen Sie dann schließlich die Ereignis-Listener hinzu und entfernen Sie sie, um sie zu aktivieren und zu deaktivieren.
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
}
handleClickOutsidein document.addEventListener()durch Zugabe von thisReferenz. Andernfalls gibt es Uncaught ReferenceError: handleClickOutside ist nicht definiert incomponentWillMount()
Super spät zur Party, aber ich hatte Erfolg damit, ein Unschärfeereignis für das übergeordnete Element der Dropdown-Liste mit dem zugehörigen Code festzulegen, um die Dropdown-Liste zu schließen, und dem übergeordneten Element einen Mousedown-Listener hinzuzufügen, der prüft, ob die Dropdown-Liste geöffnet ist oder nicht, und stoppt die Ereignisausbreitung, wenn sie geöffnet ist, damit das Unschärfeereignis nicht ausgelöst wird.
Da das Mousedown-Ereignis in die Luft sprudelt, wird verhindert, dass Mousedown bei Kindern zu Unschärfe bei den Eltern führt.
/* 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 () sollte nicht aufgerufen werden müssen, soweit ich das beurteilen kann, aber Firefox spielt ohne es aus irgendeinem Grund nicht gut. Funktioniert unter Chrome, Firefox und Safari.
Ich habe einen einfacheren Weg gefunden.
Sie müssen nur onHide(this.closeFunction)das Modal hinzufügen
<Modal onHide={this.closeFunction}>
...
</Modal>
Angenommen, Sie haben eine Funktion zum Schließen des Modals.
Verwenden Sie das ausgezeichnete React-on-Click-Outside- Mixin:
npm install --save react-onclickoutside
Und dann
var Component = React.createClass({
mixins: [
require('react-onclickoutside')
],
handleClickOutside: function(evt) {
// ...handling code goes here...
}
});