Ich suche nach einer Möglichkeit zu erkennen, ob ein Klickereignis außerhalb einer Komponente aufgetreten ist, wie in this beschrieben Artikel. jQuery next() wird verwendet, um zu sehen, ob das Ziel eines Klickereignisses das dom-Element als eines seiner Eltern hat. Wenn es eine Übereinstimmung gibt, gehört das Click-Ereignis zu einem der Kinder und wird daher nicht als außerhalb der Komponente liegend betrachtet.
Also möchte ich in meiner Komponente einen Click-Handler an das Fenster anhängen. Wenn der Handler feuert, muss ich das Ziel mit den dom-Kindern meiner Komponente vergleichen.
Das Click-Ereignis enthält Eigenschaften wie “Pfad”, die den Dom-Pfad zu enthalten scheinen, 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 Hilfsfunktion gesteckt haben … Nein?
Könnten Sie den Click-Handler an das übergeordnete Element und nicht an das Fenster anhängen?
– J. Mark Stevens
13. September 2015 um 18:47 Uhr
Wenn Sie einen Click-Handler an das übergeordnete Element anhängen, wissen Sie, wann auf dieses Element oder eines seiner untergeordneten Elemente geklickt wird, aber ich muss alle erkennen andere Stellen, auf die geklickt wird, daher muss der Handler an das Fenster angehängt werden.
– Thijs Koerselman
13. September 2015 um 18:50 Uhr
Ich habe mir den Artikel nach der vorherigen Antwort angesehen. Wie wäre es, wenn Sie einen ClickState in der obersten Komponente festlegen und Klickaktionen von den Kindern weitergeben. Dann würden Sie die Requisiten in den Kindern überprüfen, um den Zustand „Öffnen und Schließen“ zu verwalten.
– J. Mark Stevens
13. September 2015 um 19:01 Uhr
Die oberste Komponente wäre meine App. Aber die Hörkomponente ist mehrere Ebenen tief und hat keine feste Position im Dom. Ich kann unmöglich allen Komponenten in meiner App Click-Handler hinzufügen, nur weil eine von ihnen wissen möchte, ob Sie irgendwo außerhalb davon geklickt haben. Andere Komponenten sollten sich dieser Logik nicht bewusst sein, da dies schreckliche Abhängigkeiten und Boilerplate-Code erzeugen würde.
Es ist eine Weile her, seit ich diese Antwort hinzugefügt habe, und da sie immer noch auf Interesse zu stoßen scheint, dachte ich, ich würde sie auf eine aktuellere React-Version aktualisieren. Im Jahr 2021 würde ich diese Komponente so schreiben:
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 das haben, was als “Fokus“, zum Beispiel Eingabeelemente. Diese Elemente reagieren auch auf die verwischen Ereignis, wenn sie diesen Fokus verlieren.
Um jedem Element die Möglichkeit zu geben, den Fokus zu haben, stellen Sie einfach sicher, dass sein tabindex-Attribut auf etwas anderes als -1 gesetzt ist. In normalem HTML wäre das durch das Setzen von tabindex Attribut, aber in React müssen Sie verwenden tabIndex (Beachte die Hauptstadt I).
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.
Würde dies nicht zu Problemen mit der Zugänglichkeit führen? Da Sie ein zusätzliches Element fokussierbar machen, nur um zu erkennen, wann es in den Fokus geht, aber die Interaktion auf den Dropdown-Werten erfolgen sollte, nein?
– Thijs Koerselman
5. August 2016 um 8:06 Uhr
Ich habe dieses “Arbeiten” bis zu einem gewissen Grad, es ist ein cooler kleiner Hack. Mein Problem ist, dass mein Dropdown-Inhalt ist display:absolute damit das Dropdown die Größe des übergeordneten div nicht beeinflusst. Das bedeutet, wenn ich auf ein Element in der Dropdown-Liste klicke, wird der Onblur ausgelöst.
– David
7. Oktober 2016 um 17:37 Uhr
Ich hatte Probleme mit diesem Ansatz, kein Element im Dropdown-Inhalt löst keine Ereignisse wie onClick aus.
– AnimaSola
3. Januar 2017 um 5:56 Uhr
Sie können auf andere Probleme stoßen, wenn der Dropdown-Inhalt einige fokussierbare Elemente wie beispielsweise Formulareingaben enthält. Sie werden deinen Fokus stehlen, onBlur wird in Ihrem Dropdown-Container ausgelöst und expanded wird auf false gesetzt.
– Kamagatos
4. Juli 2017 um 4:47 Uhr
Informationen zum Klicken auf Elemente in Ihrem Ziel finden Sie in dieser Antwort: stackoverflow.com/a/44378829/642287
– Onosa
8. November 2018 um 15:56 Uhr
Dies sollte die akzeptierte Antwort sein. Funktionierte perfekt für Dropdown-Menüs mit absoluter Positionierung.
Und beim Exportieren meiner Komponente habe ich sie eingepackt onClickOutside():
export default onClickOutside(NameOfComponent)
Das ist es.
Gibt es konkrete Vorteile gegenüber der tabIndex/onBlur Ansatz von Pablo vorgeschlagen? Wie funktioniert die Implementierung und wie unterscheidet sich ihr Verhalten von der onBlur sich nähern?
– Mark Amery
17. Januar 2017 um 15:20 Uhr
Es ist besser, eine dedizierte Komponente zu verwenden, als einen tabIndex-Hack zu verwenden. Upvote es!
– Atul Yadav
23. Januar 2017 um 18:55 Uhr
@MarkAmery – Ich habe einen Kommentar dazu abgegeben tabIndex/onBlur sich nähern. Es funktioniert nicht, wenn das Dropdown ist position:absolutebeispielsweise ein Menü, das über anderen Inhalten schwebt.
– Beau Smith
28. Januar 2017 um 0:11 Uhr
Ein weiterer Vorteil gegenüber Tabindex besteht darin, dass die Tabindex-Lösung auch ein Blur-Ereignis auslöst, wenn Sie sich auf ein untergeordnetes Element konzentrieren
function useOuterClick(callback) {
const callbackRef = useRef(); // initialize mutable ref, which stores callback
const innerRef = useRef(); // returned to client, who marks "border" element
// update cb on each render, so second useEffect has access to current value
useEffect(() => { callbackRef.current = callback; });
useEffect(() => {
document.addEventListener("click", handleClick);
return () => document.removeEventListener("click", handleClick);
function handleClick(e) {
if (innerRef.current && callbackRef.current &&
!innerRef.current.contains(e.target)
) callbackRef.current(e);
}
}, []); // no dependencies -> stable click listener
return innerRef; // convenience for client (doesn't need to init ref himself)
}
Hier ist ein funktionierendes Beispiel:
/*
Custom Hook
*/
function useOuterClick(callback) {
const innerRef = useRef();
const callbackRef = useRef();
// set current callback in ref, before second useEffect uses it
useEffect(() => { // useEffect wrapper to be safe for concurrent mode
callbackRef.current = callback;
});
useEffect(() => {
document.addEventListener("click", handleClick);
return () => document.removeEventListener("click", handleClick);
// read most recent callback and innerRef dom node from refs
function handleClick(e) {
if (
innerRef.current &&
callbackRef.current &&
!innerRef.current.contains(e.target)
) {
callbackRef.current(e);
}
}
}, []); // no need for callback + innerRef dep
return innerRef; // return ref; client can omit `useRef`
}
/*
Usage
*/
const Client = () => {
const [counter, setCounter] = useState(0);
const innerRef = useOuterClick(e => {
// counter state is up-to-date, when handler is called
alert(`Clicked outside! Increment counter to ${counter + 1}`);
setCounter(c => c + 1);
});
return (
<div>
<p>Click outside!</p>
<div id="container" ref={innerRef}>
Inside, counter: {counter}
</div>
</div>
);
};
ReactDOM.render(<Client />, document.getElementById("root"));
iOS behandelt im Allgemeinen nur bestimmte Elemente als anklickbar. Damit äußere Klicks funktionieren, wählen Sie einen anderen Klick-Listener als aus document – nichts nach oben inklusive body. Fügen Sie zB einen Listener im React-Root hinzu div und erweitern Sie seine Höhe, wie height: 100vh, um alle externen Klicks zu erfassen. Quelle: quirksmode.org
Gibt es konkrete Vorteile gegenüber der tabIndex/onBlur Ansatz von Pablo vorgeschlagen? Wie funktioniert die Implementierung und wie unterscheidet sich ihr Verhalten von der onBlur sich nähern?
– Mark Amery
17. Januar 2017 um 15:20 Uhr
Es ist besser, eine dedizierte Komponente zu verwenden, als einen tabIndex-Hack zu verwenden. Upvote es!
– Atul Yadav
23. Januar 2017 um 18:55 Uhr
@MarkAmery – Ich habe einen Kommentar dazu abgegeben tabIndex/onBlur sich nähern. Es funktioniert nicht, wenn das Dropdown ist position:absolutebeispielsweise ein Menü, das über anderen Inhalten schwebt.
– Beau Smith
28. Januar 2017 um 0:11 Uhr
Ein weiterer Vorteil gegenüber Tabindex besteht darin, dass die Tabindex-Lösung auch ein Blur-Ereignis auslöst, wenn Sie sich auf ein untergeordnetes Element konzentrieren
– Jemar Jones
19. April 2017 um 19:24 Uhr
@BeauSmith – Vielen Dank! Meinen Tag gerettet!
– Micha
22. Januar 2020 um 16:07 Uhr
[Update] Lösung mit Reagieren ^16.8 verwenden Haken
Das ist jetzt die richtige Lösung. Wenn Sie den Fehler erhalten .contains is not a functionkann es daran liegen, dass Sie das ref-Prop an eine benutzerdefinierte Komponente und nicht an ein echtes DOM-Element wie a übergeben <div>
– willma
14. November 2018 um 0:24 Uhr
Für diejenigen, die versuchen zu bestehen ref prop zu einer benutzerdefinierten Komponente, die Sie sich vielleicht ansehen möchten React.forwardRef
– Onoja
18. Juli 2019 um 5:44 Uhr
Das funktioniert wie ein Zauber und hat meinen Tag gerettet 🙂
– Chen Ni
26. Oktober 2021 um 10:15 Uhr
Wenn Sie auf die Bildlaufleiste klicken oder sie gedrückt halten, wirkt sich dies ebenfalls aus. Wie kann man einen Klick außerhalb erkennen lassen, aber nicht die Bildlaufleiste?
– Kien
2. Januar um 2:32
9236700cookie-checkKlick außerhalb der React-Komponente erkennenyes
Könnten Sie den Click-Handler an das übergeordnete Element und nicht an das Fenster anhängen?
– J. Mark Stevens
13. September 2015 um 18:47 Uhr
Wenn Sie einen Click-Handler an das übergeordnete Element anhängen, wissen Sie, wann auf dieses Element oder eines seiner untergeordneten Elemente geklickt wird, aber ich muss alle erkennen andere Stellen, auf die geklickt wird, daher muss der Handler an das Fenster angehängt werden.
– Thijs Koerselman
13. September 2015 um 18:50 Uhr
Ich habe mir den Artikel nach der vorherigen Antwort angesehen. Wie wäre es, wenn Sie einen ClickState in der obersten Komponente festlegen und Klickaktionen von den Kindern weitergeben. Dann würden Sie die Requisiten in den Kindern überprüfen, um den Zustand „Öffnen und Schließen“ zu verwalten.
– J. Mark Stevens
13. September 2015 um 19:01 Uhr
Die oberste Komponente wäre meine App. Aber die Hörkomponente ist mehrere Ebenen tief und hat keine feste Position im Dom. Ich kann unmöglich allen Komponenten in meiner App Click-Handler hinzufügen, nur weil eine von ihnen wissen möchte, ob Sie irgendwo außerhalb davon geklickt haben. Andere Komponenten sollten sich dieser Logik nicht bewusst sein, da dies schreckliche Abhängigkeiten und Boilerplate-Code erzeugen würde.
– Thijs Koerselman
13. September 2015 um 19:14 Uhr
Ich möchte Ihnen eine sehr schöne Bibliothek empfehlen. Erstellt von AirBnb: github.com/airbnb/react-outside-click-handler
– Osoischer Marcel
5. April 2019 um 13:35 Uhr