Szukam sposobu na wykrycie, czy zdarzenie kliknięcia nastąpiło poza komponentem, jak opisano w tym artykule . Funkcja jQuery closest () służy do sprawdzania, czy element docelowy ze zdarzenia click ma element dom jako jedno z jego elementów nadrzędnych. Jeśli istnieje dopasowanie, zdarzenie click należy do jednego z elementów potomnych i dlatego nie jest uważane za znajdujące się poza komponentem.
Więc w moim komponencie chcę dołączyć moduł obsługi kliknięć do okna. Kiedy program obsługi uruchamia się, muszę porównać cel z elementami podrzędnymi dom mojego komponentu.
Zdarzenie kliknięcia zawiera właściwości takie jak „ścieżka”, która wydaje się zawierać ścieżkę dom, którą przeszło zdarzenie. Nie jestem pewien, co porównać ani jak najlepiej go przejść, i myślę, że ktoś musiał już włączyć to w sprytną funkcję użyteczności ... Nie?
źródło
Odpowiedzi:
Poniższe rozwiązanie wykorzystuje ES6 i postępuje zgodnie z najlepszymi praktykami w zakresie wiązania, a także ustawiania odwołania za pomocą metody.
Aby zobaczyć to w akcji:
Realizacja klasy:
Implementacja haków:
źródło
document.addEventListener
tutaj?context
jest niezbędny jako argument w konstruktorze, jeśli jest używany. W tym przypadku nie jest używany. Powodem, dla którego zespół reagujący zalecaprops
jako argument w konstruktorze jest to, że użyciethis.props
przed wywołaniemsuper(props)
będzie niezdefiniowane i może prowadzić do błędów.context
jest nadal dostępny w wewnętrznych węzłach, to jest cały celcontext
. W ten sposób nie musisz przekazywać go między komponentami, jak w przypadku rekwizytów. 2. Jest to tylko preferencja stylistyczna i nie uzasadnia debaty w tym przypadku.Oto rozwiązanie, które najlepiej dla mnie działało bez dołączania zdarzeń do kontenera:
Niektóre elementy HTML mogą mieć tak zwane „ fokus ”, na przykład elementy wejściowe. Elementy te będą również reagować na zdarzenie rozmycia , gdy stracą koncentrację.
Aby dowolny element mógł się skupić, upewnij się, że jego atrybut tabindex jest ustawiony na wartość inną niż -1. W zwykłym HTML byłoby to przez ustawienie
tabindex
atrybutu, ale w React musisz użyćtabIndex
(zwróć uwagę na kapitałI
).Możesz to również zrobić za pomocą JavaScript za pomocą
element.setAttribute('tabindex',0)
Do tego właśnie go użyłem, aby stworzyć niestandardowe menu DropDown.
źródło
display:absolute
taka, że rozwijana lista nie wpłynie na rozmiar div rodzica. Oznacza to, że kiedy kliknę element w menu rozwijanym, uruchomi się onblur.onBlur
zostaną uruchomione na twoim rozwijanym pojemniku iexpanded
będą ustawione na false.Utknąłem w tej samej sprawie. Jestem tu trochę spóźniony na imprezę, ale dla mnie to naprawdę dobre rozwiązanie. Mam nadzieję, że będzie to pomocne dla kogoś innego. Musisz zaimportować
findDOMNode
zreact-dom
Podejście do reakcji na haki (16,8 +)
Możesz utworzyć hak wielokrotnego użytku o nazwie
useComponentVisible
.Następnie w komponencie chcesz dodać funkcjonalność, aby wykonać następujące czynności:
Znajdź przykład codeandbox tutaj.
źródło
ReactDOM.findDOMNode
i jest przestarzałe, należy użyćref
wywołania zwrotne: github.com/yannickcr/eslint-plugin-react/issues/...Po wypróbowaniu wielu metod tutaj postanowiłem użyć github.com/Pomax/react-onclickoutside ze względu na jego kompletność.
Zainstalowałem moduł przez npm i zaimportowałem go do mojego komponentu:
Następnie w mojej klasie komponentów zdefiniowałem
handleClickOutside
metodę:A podczas eksportowania mojego komponentu zapakowałem go
onClickOutside()
:Otóż to.
źródło
tabIndex
/onBlur
podejścia zaproponowanego przez Pablo ? Jak działa wdrożenie i czym różni się jego zachowanie odonBlur
podejścia?tabIndex/onBlur
podejście. Nie działa, gdy menu rozwijane jestposition:absolute
, na przykład menu najechane na inną treść.Znalazłem rozwiązanie dzięki Benowi Alpertowi na przedyskutuj.reactjs.org . Sugerowane podejście dołącza program obsługi do dokumentu, ale okazało się to problematyczne. Kliknięcie jednego ze składników w moim drzewie spowodowało ponowne wyrenderowanie, które usunęło kliknięty element podczas aktualizacji. Ponieważ ponowne wysyłanie z React zdarza się wcześniej obsługi treści dokumentu, element nie został wykryty jako „wewnątrz” drzewa.
Rozwiązaniem tego było dodanie modułu obsługi do elementu głównego aplikacji.
Główny:
składnik:
źródło
document
nie ma go w programie?mousedown
prawdopodobnie byłby lepszym programem obsługi niżclick
tutaj. W większości aplikacji zachowanie zamykania menu poprzez kliknięcie na zewnątrz występuje w momencie, gdy najedziesz myszką, a nie w momencie zwolnienia. Wypróbuj na przykład flagę przepełnienia stosu lub dialogi udostępniania lub jedno z menu rozwijanych z górnego paska menu przeglądarki.ReactDOM.findDOMNode
i łańcuchref
są przestarzałe, należy użyćref
wywołania zwrotne: github.com/yannickcr/eslint-plugin-react/issues/...document
Wdrożenie haczyka na podstawie doskonałej rozmowy Tannera Linsleya na JSConf Hawaii 2020 :
useOuterClick
Interfejs API hookaRealizacja
useOuterClick
wykorzystuje zmienne referencje, aby stworzyć lean API dlaClient
. Oddzwonienie można ustawić bez konieczności zapamiętywania go przezuseCallback
. Ciało zwrotne nadal ma dostęp do najnowszych rekwizytów i stanu (brak nieaktualnych wartości zamknięcia).Uwaga dla użytkowników iOS
iOS traktuje tylko niektóre elementy jako klikalne. Aby obejść to zachowanie, wybierz inny odbiornik kliknięć zewnętrznych niż
document
- w górę nicbody
. Na przykład możesz dodać detektor do katalogu głównego Reactdiv
w powyższym przykładzie i zwiększyć jego wysokość (height: 100vh
lub podobny), aby złapać wszystkie kliknięcia zewnętrzne. Źródła: quirksmode.org i ta odpowiedźźródło
@media (hover: none) and (pointer: coarse) { body { cursor:pointer } }
Wydaje mi się, że dodanie tego do moich globalnych stylów rozwiązało problem.@supports (-webkit-overflow-scrolling: touch)
które dotyczy tylko IOS, chociaż nie wiem więcej, ile! Właśnie to przetestowałem i działa.Żadna z pozostałych odpowiedzi tutaj nie działała dla mnie. Próbowałem ukryć wyskakujące okienko przy rozmyciu, ale ponieważ zawartość była absolutnie ustawiona, onBlur strzelał nawet po kliknięciu zawartości wewnętrznej.
Oto podejście, które zadziałało dla mnie:
Mam nadzieję że to pomoże.
źródło
document
. Dzięki.[Aktualizacja] Rozwiązanie z React ^ 16,8 za pomocą haków
CodeSandbox
Rozwiązanie z React ^ 16,3 :
CodeSandbox
źródło
.contains is not a function
, przyczyną może być przekazanie rekwizytu referencyjnego do komponentu niestandardowego, a nie rzeczywistego elementu DOM, takiego jak<div>
ref
rekwizyt do niestandardowego komponentu, możeszReact.forwardRef
Oto moje podejście (demo - https://jsfiddle.net/agymay93/4/ ):
Stworzyłem specjalny komponent o nazwie
WatchClickOutside
i można go używać w następujący sposób (zakładamJSX
składnię):Oto kod
WatchClickOutside
komponentu:źródło
ref
jest przestarzała, należy użyćref
wywołania zwrotne: reactjs.org/docs/refs-and-the-dom.htmlTo już ma wiele odpowiedzi, ale nie dotyczą one i nie
e.stopPropagation()
zapobiegają klikaniu linków reagujących poza elementem, który chcesz zamknąć.Ze względu na fakt, że React ma własną procedurę obsługi sztucznych zdarzeń, nie można używać dokumentu jako podstawy do nasłuchiwania zdarzeń. Musisz to zrobić
e.stopPropagation()
wcześniej, ponieważ React używa samego dokumentu. Jeśli użyjesz na przykładdocument.querySelector('body')
zamiast. Jesteś w stanie zapobiec kliknięciu linku React. Poniżej znajduje się przykład tego, jak wdrażam kliknięcie na zewnątrz i zamknięcie.Używa ES6 i React 16.3 .
źródło
Dla tych, którzy potrzebują bezwzględnego pozycjonowania, prostą opcją, którą wybrałem, jest dodanie elementu opakowania, który jest zaprojektowany tak, aby pokrył całą stronę przezroczystym tłem. Następnie możesz dodać onClick na tym elemencie, aby zamknąć wewnętrzny komponent.
Tak jak jest teraz, jeśli dodasz moduł obsługi kliknięć w treści, zdarzenie zostanie również propagowane do górnego div, a zatem wyzwoli moduł handlerOutsideClick. Jeśli nie jest to pożądane zachowanie, po prostu zatrzymaj postęp zdarzenia w module obsługi.
`
źródło
Rozszerzyć na zaakceptowaną odpowiedź udzieloną przez Bena Buda , jeśli używasz stylizowanych komponentów, przekazanie referencji w ten sposób spowoduje błąd, taki jak „this.wrapperRef.contains nie jest funkcją”.
Sugerowana poprawka w komentarzach, aby owinąć stylizowany komponent div i przekazać tam referencję, działa. Powiedziawszy to, w swoich dokumentach już wyjaśniają powód tego i właściwe użycie referencji w stylizowanych komponentach:
Tak jak:
Następnie możesz uzyskać do niego bezpośredni dostęp w ramach funkcji „handleClickOutside”:
Dotyczy to również podejścia „onBlur”:
źródło
Interfejs użytkownika ma mały komponent do rozwiązania tego problemu: https://material-ui.com/components/click-away-listener/ , który można wybrać. Waży 1,5 kB zgzipowany, obsługuje telefony komórkowe, IE 11 i portale.
źródło
Moim największym zmartwieniem dla wszystkich innych odpowiedzi jest filtrowanie zdarzeń kliknięcia od katalogu głównego / rodzica w dół. Odkryłem, że najprostszym sposobem było po prostu ustawienie elementu rodzeństwa z pozycją: naprawiony, indeks Z 1 za listą rozwijaną i obsłużenie zdarzenia kliknięcia na stałym elemencie w tym samym komponencie. Utrzymuje wszystko scentralizowane w danym komponencie.
Przykładowy kod
źródło
Zdarzenie
path[0]
to ostatni kliknięty elementźródło
Korzystałem z tego modułu (nie mam skojarzeń z autorem)
Dobrze się spisał.
źródło
"Support for the experimental syntax 'classProperties' isn't currently enabled"
to po prostu wyszło z pudełka. Każdemu, kto wypróbuje to rozwiązanie, pamiętaj o usunięciu kodu, który mógł zostać skopiowany podczas wypróbowywania innych rozwiązań. np. dodanie detektorów zdarzeń wydaje się kolidować z tym.Zrobiłem to częściowo, śledząc to i postępując zgodnie z oficjalnymi dokumentami React dotyczącymi obsługi referencji, które wymagają reakcji ^ 16.3. To jedyna rzecz, która zadziałała dla mnie po wypróbowaniu innych sugestii tutaj ...
źródło
Przykład ze strategią
Podobają mi się dostarczone rozwiązania, które wykorzystują to samo, tworząc opakowanie wokół komponentu.
Ponieważ jest to bardziej zachowanie, pomyślałem o strategii i wymyśliłem następujące.
Jestem nowy z React i potrzebuję trochę pomocy, aby zaoszczędzić trochę płyty grzewczej w przypadkach użycia
Przejrzyj i powiedz mi, co myślisz.
ClickOutsideBehavior
Przykładowe użycie
Notatki
Pomyślałem o przechowywaniu
component
przechwyconych haków cyklu życia i zastąpiłem je metodami podobnymi do tego:component
jest komponentem przekazanym do konstruktoraClickOutsideBehavior
.Spowoduje to usunięcie włączania / wyłączania elementu kotłowego od użytkownika tego zachowania, ale nie wygląda to zbyt ładnie
źródło
W moim przypadku DROPDOWN rozwiązanie Bena Buda działało dobrze, ale miałem osobny przycisk przełączania z funkcją obsługi onClick. Tak więc logika klikania na zewnątrz była w konflikcie z przyciskiem na przełączniku Kliknij. Oto jak to rozwiązałem, przekazując również odnośnik przycisku:
źródło
Jeśli chcesz użyć małego komponentu (466 bajtów spakowanych gzip), który już istnieje dla tej funkcjonalności, możesz sprawdzić tę bibliotekę - reakcja-kliknięcie .
Zaletą biblioteki jest to, że pozwala ona również wykrywać kliknięcia poza komponentem i wewnątrz innego. Obsługuje również wykrywanie innych rodzajów zdarzeń.
źródło
Jeśli chcesz użyć małego komponentu (466 bajtów spakowanych gzipem), który już istnieje dla tej funkcjonalności, możesz sprawdzić bibliotekę reagującą outclick . Przechwytuje zdarzenia poza komponentem reagującym lub elementem jsx.
Zaletą biblioteki jest to, że pozwala ona również wykrywać kliknięcia poza komponentem i wewnątrz innego. Obsługuje również wykrywanie innych rodzajów zdarzeń.
źródło
Wiem, że to stare pytanie, ale ciągle się z tym spotykam i miałem problemy z rozgryzieniem tego w prostym formacie. Więc jeśli to ułatwiłoby komuś życie, użyj OutsideClickHandler przez airbnb. Jest to najprostsza wtyczka do wykonania tego zadania bez pisania własnego kodu.
Przykład:
źródło
źródło
możesz w prosty sposób rozwiązać problem, pokażę ci:
…
to działa dla mnie, powodzenia;)
źródło
Znalazłem to w poniższym artykule:
render () {return ({this.node = node;}}> Przełącz Popover {this.state.popupVisible && (Jestem popover!)}); }}
Oto świetny artykuł na ten temat: „Obsługa kliknięć poza komponentami React” https://larsgraubner.com/handle-outside-clicks-react/
źródło
Dodaj
onClick
moduł obsługi do kontenera najwyższego poziomu i zwiększaj wartość stanu za każdym razem, gdy użytkownik kliknie. Przekaż tę wartość do odpowiedniego komponentu i za każdym razem, gdy zmieni się wartość, możesz wykonać pracę.W tym przypadku dzwonimy
this.closeDropdown()
za każdym razem, gdyclickCount
zmienia się wartość.Te
incrementClickCount
pożary metoda wewnątrz.app
pojemnika, ale nie.dropdown
dlatego, że używamyevent.stopPropagation()
, aby zapobiec pęcherzyków zdarzeń.Twój kod może wyglądać mniej więcej tak:
źródło
Aby rozwiązanie „fokusowe” działało na liście rozwijanej za pomocą detektorów zdarzeń, możesz dodać je za pomocą zdarzenia onMouseDown zamiast onClick . W ten sposób zdarzenie zostanie uruchomione, a następnie wyskakujące okienko zostanie zamknięte w następujący sposób:
źródło
źródło
import ReactDOM from 'react-dom';
Zrobiłem rozwiązanie na każdą okazję.
Powinieneś użyć komponentu High Order, aby owinąć komponent, którego chcesz słuchać poza kliknięciami.
Ten przykład komponentu ma tylko jedną prop: „onClickedOutside”, która odbiera funkcję.
źródło
UseOnClickOutside Hook - React 16,8 +
Utwórz ogólną funkcję useOnOutsideClick
Następnie użyj haka w dowolnym funkcjonalnym elemencie.
Link do wersji demo
Częściowo zainspirowany odpowiedzią @ pau1fitzgerald.
źródło