Ich suche nach einer Möglichkeit, um festzustellen, ob ein Klickereignis außerhalb einer Komponente aufgetreten ist, wie in diesem Artikel beschrieben . jQuery next () wird verwendet, um festzustellen, ob das Ziel eines Klickereignisses das dom-Element als eines seiner übergeordneten Elemente hat. Wenn eine Übereinstimmung vorliegt, gehört das Klickereignis zu einem der untergeordneten Elemente und wird daher nicht als außerhalb der Komponente befindlich betrachtet.
Daher möchte ich in meiner Komponente einen Klick-Handler an das Fenster anhängen. Wenn der Handler ausgelöst wird, muss ich das Ziel mit den dom-Kindern meiner Komponente vergleichen.
Das Klickereignis enthält Eigenschaften wie "Pfad", der den Dom-Pfad zu enthalten scheint, den das Ereignis zurückgelegt hat. Ich bin mir nicht sicher, was ich vergleichen oder wie ich es am besten durchqueren soll, und ich denke, jemand muss das bereits in eine clevere Utility-Funktion gesteckt haben ... Nein?
quelle
Antworten:
Refs Verwendung in React 16.3+ geändert.
Die folgende Lösung verwendet ES6 und befolgt Best Practices zum Binden sowie zum Festlegen des Verweises durch eine Methode.
Um es in Aktion zu sehen:
Klassenimplementierung:
Implementierung von Hooks:
quelle
document.addEventListener
hier verwendeten verwenden?context
ist nur dann als Argument im Konstruktor erforderlich, wenn es verwendet wird. Es wird in diesem Fall nicht verwendet. Der Grund, den das Reaktionsteamprops
als Argument im Konstruktor empfiehlt, ist, dass die Verwendung vonthis.props
vor dem Aufrufsuper(props)
undefiniert ist und zu Fehlern führen kann.context
ist immer noch auf inneren Knoten verfügbar, das ist der ganze Zweck voncontext
. Auf diese Weise müssen Sie es nicht wie bei Requisiten von Komponente zu Komponente weitergeben. 2. Dies ist nur eine stilistische Präferenz und rechtfertigt in diesem Fall keine Debatte.Hier ist die Lösung, die für mich am besten funktioniert hat, ohne Ereignisse an den Container anzuhängen:
Bestimmte HTML-Elemente können einen sogenannten " Fokus " haben, beispielsweise Eingabeelemente. Diese Elemente reagieren auch auf das Unschärfeereignis , wenn sie diesen Fokus verlieren.
Um einem Element die Fähigkeit zu geben, sich zu konzentrieren, stellen Sie einfach sicher, dass sein tabindex-Attribut auf etwas anderes als -1 gesetzt ist. In normalem HTML würde dies durch Setzen des
tabindex
Attributs geschehen, aber in React müssen Sie verwendentabIndex
(beachten Sie das GroßbuchstabenI
).Sie können dies auch über JavaScript mit tun
element.setAttribute('tabindex',0)
Dafür habe ich es verwendet, um ein benutzerdefiniertes DropDown-Menü zu erstellen.
quelle
display:absolute
so ist , dass das Dropdown die Größe des übergeordneten Divs nicht beeinflusst. Das heißt, wenn ich auf ein Element in der Dropdown-Liste klicke, wird die Unschärfe ausgelöst.onBlur
werden auf Ihrem Dropdown-Container ausgelöst undexpanded
auf false gesetzt.Ich war in der gleichen Frage festgefahren. Ich bin etwas spät zur Party hier, aber für mich ist das eine wirklich gute Lösung. Hoffentlich hilft es jemand anderem. Sie müssen Import
findDOMNode
vonreact-dom
React Hooks Approach (16.8 +)
Sie können einen wiederverwendbaren Hook namens erstellen
useComponentVisible
.Dann möchten Sie in der Komponente die Funktionalität hinzufügen, um Folgendes zu tun:
Hier finden Sie ein Code- und Box- Beispiel.
quelle
ReactDOM.findDOMNode
und ist veraltet, sollteref
Rückrufe verwenden: github.com/yannickcr/eslint-plugin-react/issues/…Nachdem ich hier viele Methoden ausprobiert hatte, entschied ich mich, github.com/Pomax/react-onclickoutside zu verwenden, da es vollständig ist.
Ich habe das Modul über npm installiert und in meine Komponente importiert:
Dann habe ich in meiner Komponentenklasse die
handleClickOutside
Methode definiert :Und beim Exportieren meiner Komponente habe ich sie eingepackt
onClickOutside()
:Das ist es.
quelle
tabIndex
/onBlur
Ansatz vorgeschlagen von Pablo ? Wie funktioniert die Implementierung und wie unterscheidet sich ihr Verhalten vomonBlur
Ansatz?tabIndex/onBlur
Ansatz abgegeben. Es funktioniert nicht, wenn sich das Dropdown-Menü befindetposition:absolute
, z. B. wenn ein Menü über anderen Inhalten schwebt.Ich habe dank Ben Alpert auf diskut.reactjs.org eine Lösung gefunden . Der vorgeschlagene Ansatz fügt dem Dokument einen Handler hinzu, der sich jedoch als problematisch herausstellte. Das Klicken auf eine der Komponenten in meinem Baum führte zu einem erneuten Rendern, bei dem das angeklickte Element beim Aktualisieren entfernt wurde. Da das erneute Rendern von React erfolgt, bevor der Dokumentkörper-Handler aufgerufen wird, wurde das Element nicht als "innerhalb" des Baums erkannt.
Die Lösung hierfür bestand darin, den Handler zum Anwendungsstammelement hinzuzufügen.
Main:
Komponente:
quelle
document
nicht ist?mousedown
wäre wahrscheinlich ein besserer Handler alsclick
hier. In den meisten Anwendungen tritt das Verhalten beim Schließen des Menüs durch Klicken nach außen in dem Moment auf, in dem Sie mit der Maus nach unten drücken, nicht beim Loslassen. Versuchen Sie es, zum Beispiel, mit Stapelüberlauf der Flagge oder Aktien Dialogen oder mit einem der Dropdown - Listen in der oberen Menüleiste des Browsers.ReactDOM.findDOMNode
und Zeichenfolgeref
sind veraltet, solltenref
Rückrufe verwenden: github.com/yannickcr/eslint-plugin-react/issues/…document
Hook-Implementierung basierend auf Tanner Linsleys ausgezeichnetem Vortrag auf der JSConf Hawaii 2020 :
useOuterClick
Hook-APIImplementierung
useOuterClick
verwendet veränderbare Verweise , um eine schlanke API für die zu erstellenClient
. Ein Rückruf kann eingestellt werden, ohne dass Sie ihn über merken müssenuseCallback
. Der Rückrufkörper hat weiterhin Zugriff auf die neuesten Requisiten und den neuesten Status (keine veralteten Schließungswerte).Hinweis für iOS-Benutzer
iOS behandelt nur bestimmte Elemente als anklickbar. Um dieses Verhalten zu umgehen, wählen Sie einen anderen äußeren Klick-Listener als
document
- nichts nach oben, einschließlichbody
. Sie könnendiv
beispielsweise im obigen Beispiel einen Listener zum React-Stamm hinzufügen und dessen Höhe (height: 100vh
oder ähnlich) erweitern, um alle externen Klicks zu erfassen. Quellen: quirksmode.org und diese Antwortquelle
@media (hover: none) and (pointer: coarse) { body { cursor:pointer } }
Das Hinzufügen in meinen globalen Stilen scheint das Problem behoben zu haben.@supports (-webkit-overflow-scrolling: touch)
, die nur auf IOS abzielt, obwohl ich nicht mehr weiß, wie viel! Ich habe es gerade getestet und es funktioniert.Keine der anderen Antworten hier hat bei mir funktioniert. Ich habe versucht, ein Popup bei Unschärfe auszublenden, aber da der Inhalt absolut positioniert war, wurde der onBlur auch beim Klicken auf den inneren Inhalt ausgelöst.
Hier ist ein Ansatz, der für mich funktioniert hat:
Hoffe das hilft.
quelle
document
. Vielen Dank.[Update] Lösung mit React ^ 16.8 mit Hooks
CodeSandbox
Lösung mit Reaktion ^ 16.3 :
CodeSandbox
quelle
.contains is not a function
, kann es sein, dass Sie die Ref-Requisite an eine benutzerdefinierte Komponente übergeben und nicht an ein echtes DOM-Element wie ein<div>
ref
Requisiten an eine benutzerdefinierte Komponente zu übergeben, möchten Sie vielleicht einen Blick darauf werfenReact.forwardRef
Hier ist mein Ansatz (Demo - https://jsfiddle.net/agymay93/4/ ):
Ich habe eine spezielle Komponente namens erstellt
WatchClickOutside
und sie kann wie folgt verwendet werden (ich nehme dieJSX
Syntax an):Hier ist der Code der
WatchClickOutside
Komponente:quelle
ref
ist veraltet, sollteref
Rückrufe verwenden: reactjs.org/docs/refs-and-the-dom.htmlDies hat bereits viele Antworten, aber sie adressieren
e.stopPropagation()
und verhindern nicht das Klicken auf Reaktionslinks außerhalb des Elements, das Sie schließen möchten.Aufgrund der Tatsache, dass React über einen eigenen künstlichen Ereignishandler verfügt, können Sie das Dokument nicht als Basis für Ereignis-Listener verwenden. Sie müssen dies
e.stopPropagation()
vorher tun, da React das Dokument selbst verwendet. Wenn Siedocument.querySelector('body')
stattdessen zum Beispiel verwenden. Sie können das Klicken über den Link Reagieren verhindern. Im Folgenden finden Sie ein Beispiel für die Implementierung von Click Outside und Close.Dies verwendet ES6 und React 16.3 .
quelle
Für diejenigen, die eine absolute Positionierung benötigen, ist eine einfache Option, für die ich mich entschieden habe, das Hinzufügen einer Wrapper-Komponente, die so gestaltet ist, dass sie die gesamte Seite mit einem transparenten Hintergrund abdeckt. Anschließend können Sie einen onClick auf dieses Element hinzufügen, um Ihre interne Komponente zu schließen.
Wie im Moment, wenn Sie einen Klick-Handler für Inhalte hinzufügen, wird das Ereignis auch an das obere Div weitergegeben und löst daher den handlerOutsideClick aus. Wenn dies nicht Ihr gewünschtes Verhalten ist, stoppen Sie einfach die Ereignisprogation auf Ihrem Handler.
`
quelle
Um die akzeptierte Antwort von Ben Bud zu erweitern , wenn Sie gestylte Komponenten verwenden, erhalten Sie beim Übergeben von Refs auf diese Weise einen Fehler wie "this.wrapperRef.contains ist keine Funktion".
Die in den Kommentaren vorgeschlagene Korrektur, die gestaltete Komponente mit einem Div zu versehen und den Ref dort zu übergeben, funktioniert. Allerdings erklären sie in ihren Dokumenten bereits den Grund dafür und die ordnungsgemäße Verwendung von Refs in gestalteten Komponenten:
Wie so:
Dann können Sie direkt über die Funktion "handleClickOutside" darauf zugreifen:
Dies gilt auch für den "onBlur" -Ansatz:
quelle
Material-UI hat eine kleine Komponente, um dieses Problem zu lösen: https://material-ui.com/components/click-away-listener/ , die Sie auswählen können. Es wiegt 1,5 kB gezippt und unterstützt Mobile, IE 11 und Portale.
quelle
Mein größtes Anliegen bei allen anderen Antworten ist es, Klickereignisse vom Stammverzeichnis / übergeordneten Element nach unten zu filtern. Ich fand, der einfachste Weg war, einfach ein Geschwisterelement mit der Position festzusetzen: fest, einen Z-Index 1 hinter dem Dropdown und das Klickereignis auf das feste Element innerhalb derselben Komponente zu behandeln. Hält alles zentral für eine bestimmte Komponente.
Beispielcode
quelle
Ereignis
path[0]
ist das letzte angeklickte Elementquelle
Ich habe dieses Modul verwendet (ich habe keine Verbindung zum Autor)
Es hat den Job gut gemacht.
quelle
"Support for the experimental syntax 'classProperties' isn't currently enabled"
funktioniert hat. Denken Sie bei jedem Versuch mit dieser Lösung daran, den verbleibenden Code zu löschen, den Sie möglicherweise kopiert haben, wenn Sie die anderen Lösungen ausprobieren. Das Hinzufügen von Ereignis-Listenern scheint dem zu widersprechen.Ich habe dies teilweise getan, indem ich dies befolgt habe und indem ich den offiziellen React-Dokumenten zum Umgang mit Refs gefolgt bin, die eine Reaktion erfordern ^ 16.3. Dies ist das einzige, was für mich funktioniert hat, nachdem ich einige der anderen Vorschläge hier ausprobiert habe ...
quelle
Ein Beispiel mit Strategie
Ich mag die bereitgestellten Lösungen, die das Gleiche tun, indem sie einen Wrapper um die Komponente erstellen.
Da dies eher ein Verhalten ist, dachte ich an Strategie und kam auf Folgendes.
Ich bin neu bei React und brauche ein bisschen Hilfe, um in den Anwendungsfällen etwas Boilerplate zu sparen
Bitte überprüfen Sie und sagen Sie mir, was Sie denken.
ClickOutsideBehavior
Beispielnutzung
Anmerkungen
Ich dachte daran, die übergebenen
component
Lifecycle-Hooks zu speichern und sie mit ähnlichen Methoden zu überschreiben:component
ist die Komponente, die an den Konstruktor von übergeben wirdClickOutsideBehavior
.Dadurch wird das Aktivierungs- / Deaktivierungs-Boilerplate vom Benutzer dieses Verhaltens entfernt, aber es sieht nicht besonders gut aus
quelle
In meinem DROPDOWN-Fall funktionierte die Lösung von Ben Bud gut, aber ich hatte eine separate Umschalttaste mit einem onClick-Handler. Die äußere Klicklogik widersprach also der Schaltfläche onClick toggler. Hier ist, wie ich es gelöst habe, indem ich auch den Ref des Buttons übergeben habe:
quelle
Wenn Sie eine winzige Komponente (466 Byte gzipped) verwenden möchten, die für diese Funktionalität bereits vorhanden ist, können Sie diesen React-Outclick dieser Bibliothek überprüfen .
Das Gute an der Bibliothek ist, dass Sie damit auch Klicks außerhalb einer Komponente und innerhalb einer anderen Komponente erkennen können. Es unterstützt auch das Erkennen anderer Arten von Ereignissen.
quelle
Wenn Sie eine winzige Komponente (466 Byte gzipped) verwenden möchten, die für diese Funktionalität bereits vorhanden ist, können Sie diesen React-Outclick dieser Bibliothek überprüfen . Es erfasst Ereignisse außerhalb einer Reaktionskomponente oder eines jsx-Elements.
Das Gute an der Bibliothek ist, dass Sie damit auch Klicks außerhalb einer Komponente und innerhalb einer anderen Komponente erkennen können. Es unterstützt auch das Erkennen anderer Arten von Ereignissen.
quelle
Ich weiß, dass dies eine alte Frage ist, aber ich stoße immer wieder darauf und hatte große Probleme, dies in einem einfachen Format herauszufinden. Wenn dies jemandem das Leben ein bisschen leichter machen würde, verwenden Sie OutsideClickHandler von airbnb. Es ist das einfachste Plugin, um diese Aufgabe zu erledigen, ohne eigenen Code zu schreiben.
Beispiel:
quelle
quelle
Sie können Ihnen einen einfachen Weg, um Ihr Problem zu lösen, ich zeige Ihnen:
....
das funktioniert bei mir, viel glück;)
quelle
Ich fand dies aus dem Artikel unten:
render () {return ({this.node = node;}}> Popover umschalten {this.state.popupVisible && (Ich bin ein Popover!)}); }}
Hier ist ein großartiger Artikel zu diesem Problem: "Klicks außerhalb von React-Komponenten verarbeiten" https://larsgraubner.com/handle-outside-clicks-react/
quelle
Fügen Sie
onClick
Ihrem Container der obersten Ebene einen Handler hinzu und erhöhen Sie einen Statuswert, wenn der Benutzer darauf klickt. Übergeben Sie diesen Wert an die entsprechende Komponente, und wenn sich der Wert ändert, können Sie arbeiten.In diesem Fall rufen wir auf,
this.closeDropdown()
wenn sich derclickCount
Wert ändert.Die
incrementClickCount
Methode wird innerhalb des.app
Containers ausgelöst, jedoch nicht,.dropdown
weil wir sie verwendenevent.stopPropagation()
, um das Sprudeln von Ereignissen zu verhindern.Ihr Code sieht möglicherweise folgendermaßen aus:
quelle
Damit die "Fokus" -Lösung für Dropdown-Listen mit Ereignis-Listenern funktioniert, können Sie sie mit dem onMouseDown- Ereignis anstelle von onClick hinzufügen . Auf diese Weise wird das Ereignis ausgelöst und danach wird das Popup wie folgt geschlossen:
quelle
quelle
import ReactDOM from 'react-dom';
Ich habe für alle Gelegenheiten eine Lösung gefunden.
Sie sollten eine Komponente höherer Ordnung verwenden, um die Komponente, die Sie auf Klicks außerhalb der Komponente warten möchten, zu verpacken.
Dieses Komponentenbeispiel enthält nur eine Requisite: "onClickedOutside", die eine Funktion empfängt.
quelle
UseOnClickOutside Hook - Reagiere 16.8 +
Erstellen Sie eine allgemeine useOnOutsideClick-Funktion
Verwenden Sie dann den Haken in einer beliebigen Funktionskomponente.
Link zur Demo
Teilweise inspiriert von @ pau1fitzgerald Antwort.
quelle