Mam kilka menu HTML, które wyświetlam całkowicie, gdy użytkownik kliknie ich górną część. Chciałbym ukryć te elementy, gdy użytkownik kliknie poza obszarem menu.
Czy coś takiego jest możliwe w jQuery?
$("#menuscontainer").clickOutsideThisElement(function() {
// Hide the menus
});
javascript
jquery
click
Sergio del Amo
źródło
źródło
event.target
i bezevent.stopPropagation
.Odpowiedzi:
Dołącz zdarzenie click do treści dokumentu, która zamyka okno. Dołącz osobne zdarzenie kliknięcia do kontenera, które zatrzymuje propagację do treści dokumentu.
źródło
$('html').click()
ciała. Ciało zawsze ma wysokość swojej zawartości. Nie ma dużo treści lub ekran jest bardzo wysoki, działa tylko na części wypełnionej ciałem.Możesz nasłuchiwać zdarzenia kliknięcia ,
document
a następnie upewnić się, że#menucontainer
nie jest on przodkiem ani celem klikniętego elementu, używając.closest()
.Jeśli tak nie jest, kliknięty element znajduje się na zewnątrz
#menucontainer
i można go bezpiecznie ukryć.Edycja - 23.06.2017
Możesz także posprzątać po detektorze zdarzeń, jeśli planujesz zamknąć menu i chcesz przestać nasłuchiwać zdarzeń. Ta funkcja wyczyści tylko nowo utworzony detektor, zachowując wszystkie inne detektory kliknięć
document
. Ze składnią ES2015:Edytuj - 2018-03-11
Dla tych, którzy nie chcą korzystać z jQuery. Oto powyższy kod w postaci zwykłego vanillaJS (ECMAScript6).
UWAGA: Jest to oparte na komentarzu Alexa, którego należy użyć
!element.contains(event.target)
zamiast części jQuery.Ale
element.closest()
jest teraz dostępny również we wszystkich głównych przeglądarkach (wersja W3C różni się nieco od wersji jQuery). Polyfills można znaleźć tutaj: Element.closest ()Edytuj - 2020-05-21
W przypadku, gdy chcesz, aby użytkownik mógł klikać i przeciągać wewnątrz elementu, a następnie zwolnij mysz poza elementem, nie zamykając elementu:
I w
outsideClickListener
:źródło
!element.contains(event.target)
za pomocą Node.contains ()To pytanie jest tak popularne i zawiera tak wiele odpowiedzi, że jest zwodniczo złożone. Po prawie ośmiu latach i dziesiątkach odpowiedzi jestem naprawdę zaskoczony, widząc, jak mało uwagi poświęcono dostępności.
To szlachetna przyczyna i faktyczny problem. Tytuł pytania - na które większość odpowiedzi wydaje się próbować odpowiedzieć - zawiera niefortunny czerwony śledź.
Wskazówka: to słowo „kliknij” !
W rzeczywistości nie chcesz wiązać programów obsługi kliknięć.
Jeśli wiążesz procedury obsługi kliknięć w celu zamknięcia okna, już nie powiodło się. Powodem niepowodzenia jest to, że nie wszyscy wyzwalają
click
zdarzenia. Użytkownicy nieużywający myszy będą mogli wyjść z twojego okna dialogowego (a twoje menu podręczne jest prawdopodobnie rodzajem okna dialogowego) przez naciśnięcie Tab, a następnie nie będą mogli odczytać treści za oknem dialogowym bez wywołaniaclick
zdarzenia.Przeformułujmy więc pytanie.
To jest cel. Niestety, teraz musimy powiązać
userisfinishedwiththedialog
wydarzenie, a wiązanie to nie jest takie proste.Jak więc wykryć, że użytkownik zakończył korzystanie z okna dialogowego?
focusout
zdarzenieDobrym początkiem jest ustalenie, czy fokus opuścił okno dialogowe.
Wskazówka: bądź ostrożny ze
blur
zdarzeniem,blur
nie rozprzestrzenia się, jeśli wydarzenie było związane z fazą bulgotania!jQuery's
focusout
będzie w porządku. Jeśli nie możesz użyć jQuery, możesz użyćblur
podczas fazy przechwytywania:Ponadto w wielu oknach dialogowych musisz pozwolić na skupienie się kontenera. Dodaj,
tabindex="-1"
aby okno dialogowe mogło dynamicznie otrzymywać fokus, nie zakłócając w inny sposób operacji tabulacji.Jeśli grasz w tę wersję demo przez ponad minutę, powinieneś szybko zacząć widzieć problemy.
Po pierwsze, link w oknie dialogowym nie jest klikalny. Próba kliknięcia na nią lub karty spowoduje zamknięcie okna dialogowego przed interakcją. Wynika to z tego, że skupienie elementu wewnętrznego wyzwala
focusout
zdarzenie przed ponownym wywołaniemfocusin
zdarzenia.Rozwiązaniem jest kolejkowanie zmiany stanu w pętli zdarzeń. Można to zrobić za pomocą
setImmediate(...)
lubsetTimeout(..., 0)
w przeglądarkach, które nie obsługująsetImmediate
. Po umieszczeniu w kolejce można go anulować poprzezfocusin
:Pokaż fragment kodu
Drugi problem polega na tym, że okno dialogowe nie zamyka się po ponownym naciśnięciu łącza. Wynika to z faktu, że okno dialogowe traci fokus, wyzwalając zachowanie zamknięcia, po czym kliknięcie łącza powoduje ponowne otwarcie okna dialogowego.
Podobnie jak w poprzednim wydaniu, stanem skupienia należy zarządzać. Biorąc pod uwagę, że zmiana stanu została już umieszczona w kolejce, jest to tylko kwestia obsługi zdarzeń skupienia w wyzwalaczach dialogu:
To powinno wyglądać znajomoPokaż fragment kodu
Esc klucz
Jeśli uważasz, że skończyłeś przez obsługę stanów skupienia, możesz zrobić więcej, aby uprościć obsługę.
Jest to często funkcja „miło mieć”, ale często, gdy masz modalne lub wyskakujące okienko, Escklucz go zamyka.
Pokaż fragment kodu
Jeśli wiesz, że w oknie dialogowym znajdują się elementy, które można aktywować, nie trzeba bezpośrednio ustawiać ostrości w oknie dialogowym. Jeśli budujesz menu, możesz zamiast tego skoncentrować pierwszy element menu.
Pokaż fragment kodu
WAI-ARIA Obsługa ról i inne ułatwienia dostępu
Mam nadzieję, że ta odpowiedź obejmuje podstawy dostępnej obsługi klawiatury i myszy dla tej funkcji, ale ponieważ jest już dość spora, uniknę dyskusji o rolach i atrybutach WAI-ARIA , jednak gorąco polecam, aby implementatorzy odnieśli się do specyfikacji w celu uzyskania szczegółowych informacji na temat ról, których powinni używać i wszelkich innych odpowiednich atrybutów.
źródło
Inne rozwiązania tutaj nie działały, więc musiałem użyć:
źródło
&& !$(event.target).parents("#foo").is("#foo")
wewnątrzIF
instrukcji, aby żadne elementy potomne nie zamykały menu po kliknięciu..is('#foo, #foo *')
, ale nie polecam wiązania procedur obsługi kliknięć, aby rozwiązać ten problem .!$(event.target).closest("#foo").length
byłoby lepiej i eliminuje potrzebę dodawania @ honyovk.Mam aplikację, która działa podobnie do przykładu Erana, z tym wyjątkiem, że dołączam zdarzenie click do ciała, kiedy otwieram menu ... Trochę tak:
Więcej informacji o funkcji jQuery
one()
źródło
.one
- wewnątrz modułu$('html')
obsługi - napisz$('html').off('click')
.one
obsługi wywoła automatycznieoff
(jak pokazano w dokumentach jQuery).Po badaniach znalazłem trzy działające rozwiązania (zapomniałem linków do stron w celach informacyjnych)
Pierwsze rozwiązanie
Drugie rozwiązanie
Trzecie rozwiązanie
źródło
document.getElementsByClassName
, jeśli ktoś ma jakąś wskazówkę, proszę się nią podzielić.Działa dla mnie dobrze.
źródło
#menucontainer
pytaniem dotyczyło kliknięciatabindex="-1"
do#menuscontainer
, aby to działało. Wygląda na to, że jeśli umieścisz znacznik wejściowy w pojemniku i klikniesz na niego, pojemnik zostanie ukryty.Teraz jest do tego wtyczka: zdarzenia zewnętrzne ( post na blogu )
Następujące dzieje się, gdy moduł obsługi Clickoutside (WLOG) jest powiązany z elementem:
W związku z tym żadne zdarzenia nie są zatrzymywane przed propagacją i dodatkowe funkcje obsługi kliknięć mogą być używane „nad” elementem z zewnętrzną funkcją obsługi.
źródło
$( '#element' ).on( 'clickoutside', function( e ) { .. } );
To działało dla mnie idealnie !!
źródło
Nie sądzę, że tak naprawdę potrzebujesz zamknąć menu, gdy użytkownik kliknie na zewnątrz; potrzebne jest zamknięcie menu, gdy użytkownik kliknie dowolne miejsce na stronie. Po kliknięciu menu lub wyłączeniu menu powinno się zamknąć, prawda?
Nie znalazłem powyżej zadowalających odpowiedzi skłoniło mnie do napisania tego postu na blogu innego dnia. Dla bardziej pedantycznych jest kilka błędów, na które należy zwrócić uwagę:
body { margin-left:auto; margin-right: auto; width:960px;}
źródło
Jak napisano w innym plakacie, jest wiele gotchas, zwłaszcza jeśli wyświetlany element (w tym przypadku menu) zawiera elementy interaktywne. Znalazłem następującą metodę, która jest dość solidna:
źródło
Prostym rozwiązaniem tej sytuacji jest:
Powyższy skrypt ukryje,
div
jeślidiv
zdarzenie poza kliknięciem zostanie wyzwolone.Aby uzyskać więcej informacji, zobacz następujący blog: http://www.codecanal.com/detect-click-outside-div-using-javascript/
źródło
Rozwiązanie 1
Zamiast używać event.stopPropagation (), który może mieć pewne skutki uboczne, wystarczy zdefiniować prostą zmienną flagi i dodać jeden
if
warunek. Przetestowałem to i działałem poprawnie bez żadnych skutków ubocznych zatrzymania Rozmnażanie:Rozwiązanie 2
Z prostym
if
warunkiem:źródło
Sprawdź cel zdarzenia kliknięcia w oknie (powinien się propagować do okna, o ile nie zostanie przechwycony nigdzie indziej) i upewnij się, że nie jest to żaden z elementów menu. Jeśli nie, oznacza to, że jesteś poza swoim menu.
Lub sprawdź pozycję kliknięcia i sprawdź, czy jest ono zawarte w obszarze menu.
źródło
Miałem sukces z czymś takim:
Logika jest następująca: gdy
#menuscontainer
jest pokazany, powiąż moduł obsługi kliknięć z ciałem, które ukrywa się#menuscontainer
tylko wtedy, gdy cel (kliknięcia) nie jest jego dzieckiem.źródło
Jako wariant:
Nie ma problemu z zatrzymaniem propagacji zdarzeń i lepiej obsługuje wiele menu na tej samej stronie, gdzie kliknięcie drugiego menu, gdy pierwsze jest otwarte, pozostawi pierwsze otwarte w rozwiązaniu stopPropagation.
źródło
Zdarzenie ma właściwość o nazwie event.path elementu, który jest „statyczną uporządkowaną listą wszystkich jego przodków w kolejności drzew” . Aby sprawdzić, czy zdarzenie pochodzi z określonego elementu DOM lub jednego z jego elementów potomnych, po prostu sprawdź ścieżkę do tego konkretnego elementu DOM. Można go również użyć do sprawdzenia wielu elementów poprzez logiczne
OR
sprawdzenie kontroli elementu wsome
funkcji.Tak powinno być w twoim przypadku
źródło
event.path
to nie jest rzecz.Znalazłem tę metodę w jakiejś wtyczce kalendarza jQuery.
źródło
Oto waniliowe rozwiązanie JavaScript dla przyszłych przeglądających.
Po kliknięciu dowolnego elementu w dokumencie, jeśli identyfikator klikniętego elementu jest przełączany lub ukryty element nie jest ukryty, a ukryty element nie zawiera klikniętego elementu, przełącz element.
Pokaż fragment kodu
Jeśli zamierzasz mieć wiele przełączników na tej samej stronie, możesz użyć czegoś takiego:
hidden
do elementu składanego.źródło
Dziwi mnie, że nikt tak naprawdę nie potwierdził
focusout
wydarzenia:źródło
Jeśli piszesz skrypty dla IE i FF 3. * i chcesz po prostu wiedzieć, czy kliknięcie nastąpiło w określonym obszarze pola, możesz również użyć czegoś takiego:
źródło
Zamiast używać przerwania przepływu, zdarzenia rozmycia / skupienia lub innych trudnych technik, po prostu dopasuj przepływ zdarzeń do pokrewieństwa elementu:
Aby usunąć kliknięcie zewnętrznego detektora zdarzeń, po prostu:
źródło
#menuscontainer
, wciąż przechodzicie przez jego rodziców. powinieneś to najpierw sprawdzić, a jeśli to nie jest ten element, to przejdź do drzewa DOM.if(!($(event.target).is("#menuscontainer") || $(event.target).parents().is("#menuscontainer"))){
. Jest to drobna optymalizacja, ale występuje tylko kilka razy w trakcie trwania programu: dla każdego kliknięcia, jeśliclick.menu-outside
zdarzenie jest zarejestrowane. Jest dłuższy (+32 znaków) i nie używaj łączenia metodPosługiwać się:
źródło
Jeśli ktoś tutaj jest ciekawy to rozwiązanie javascript (es6):
i es5, na wszelki wypadek:
});
źródło
Oto proste rozwiązanie według czystego javascript. Jest na bieżąco z ES6 :
źródło
() => {}
zamiast tego robi się tylko ES6function() {}
. To, co tam masz, jest klasyfikowane jako zwykły JavaScript z dodatkiem ES6.Zaczep detektor zdarzeń kliknięcia w dokumencie. Wewnątrz detektora zdarzeń możesz spojrzeć na obiekt zdarzenia , w szczególności event.target, aby zobaczyć, który element został kliknięty:
źródło
Użyłem poniżej skryptu i skończyłem z jQuery.
Poniżej znajdź kod HTML
Samouczek możesz przeczytać tutaj
źródło
Jeśli klikniesz dokument, ukryj dany element, chyba że klikniesz ten sam element.
źródło
Głosuj za najpopularniejszą odpowiedzią, ale dodaj
więc kliknięcie paska przewijania nie powoduje [ukrycia ani żadnego innego] elementu docelowego.
źródło
Dla łatwiejszego użycia i bardziej ekspresyjnego kodu stworzyłem wtyczkę jQuery w tym celu:
Uwaga: cel to element, który użytkownik kliknął. Ale wywołanie zwrotne jest nadal wykonywane w kontekście oryginalnego elementu, więc możesz użyć tego, jak można oczekiwać w wywołaniu zwrotnym jQuery.
Podłącz:
Domyślnie detektor zdarzenia kliknięcia jest umieszczany w dokumencie. Jeśli jednak chcesz ograniczyć zasięg detektora zdarzeń, możesz przekazać obiekt jQuery reprezentujący element poziomu nadrzędnego, który będzie najwyższym elementem nadrzędnym, w którym będą nasłuchiwane kliknięcia. Zapobiega to niepotrzebnemu nasłuchiwaniu zdarzeń na poziomie dokumentu. Oczywiście nie będzie działać, chyba że podany element nadrzędny jest rodzicem elementu początkowego.
Użyj tak:
źródło