Wszędzie szukałem dobrego rozwiązania, ale nie mogę znaleźć takiego, które nie korzysta z jQuery .
Czy istnieje normalny sposób (bez dziwnych hacków lub łatwego do złamania kodu) między przeglądarkami, aby wykryć kliknięcie poza elementem (który może mieć dzieci lub nie)?
javascript
Tiberiu-Ionuț Stan
źródło
źródło
Odpowiedzi:
Dodaj detektor zdarzeń do
document
i użyj go,Node.contains()
aby dowiedzieć się, czy cel zdarzenia (czyli najbardziej kliknięty element w środku) znajduje się wewnątrz określonego elementu. Działa nawet w IE5var specifiedElement = document.getElementById('a'); //I'm using "click" but it works with any event document.addEventListener('click', function(event) { var isClickInside = specifiedElement.contains(event.target); if (!isClickInside) { //the click was outside the specifiedElement, do something } });
Pokaż fragment kodu
var specifiedElement = document.getElementById('a'); //I'm using "click" but it works with any event document.addEventListener('click', function(event) { var isClickInside = specifiedElement.contains(event.target); if (isClickInside) { alert('You clicked inside A') } else { alert('You clicked outside A') } });
div { margin: auto; padding: 1em; max-width: 6em; background: rgba(0, 0, 0, .2); text-align: center; }
Is the click inside A or outside? <div id="a">A <div id="b">B <div id="c">C</div> </div> </div>
źródło
Musisz obsłużyć zdarzenie kliknięcia na poziomie dokumentu. W obiekcie zdarzenia masz
target
właściwość, najbardziej wewnętrzny element DOM, który został kliknięty. Dzięki temu sprawdzasz się i podchodzisz do jego elementów nadrzędnych, aż do elementu dokumentu, jeśli jeden z nich jest Twoim obserwowanym elementem.Zobacz przykład na jsFiddle
document.addEventListener("click", function (e) { var level = 0; for (var element = e.target; element; element = element.parentNode) { if (element.id === 'x') { document.getElementById("out").innerHTML = (level ? "inner " : "") + "x clicked"; return; } level++; } document.getElementById("out").innerHTML = "not x clicked"; });
Jak zawsze, to nie jest kompatybilne z różnymi przeglądarkami z powodu addEventListener / attachEvent, ale działa w ten sposób.
Dziecko jest klikane, gdy nie jest to event.target, ale jeden z jego rodziców jest obserwowanym elementem (po prostu liczę dla tego poziom). Możesz również mieć zmienną boolowską, jeśli element zostanie znaleziony lub nie, aby nie zwracać procedury obsługi z klauzuli for. Mój przykład ogranicza się do tego, że program obsługi kończy pracę tylko wtedy, gdy nic nie pasuje.
Dodając kompatybilność z różnymi przeglądarkami, zwykle robię to w ten sposób:
var addEvent = function (element, eventName, fn, useCapture) { if (element.addEventListener) { element.addEventListener(eventName, fn, useCapture); } else if (element.attachEvent) { element.attachEvent(eventName, function (e) { fn.apply(element, arguments); }, useCapture); } };
Jest to kod kompatybilny z różnymi przeglądarkami do dołączania detektora / obsługi zdarzeń, włącznie z przepisywaniem
this
w IE, jako element, tak jak robi to jQuery dla swoich programów obsługi zdarzeń. Istnieje wiele argumentów, aby mieć na uwadze kilka bitów jQuery;)źródło
target
właściwość (która, jak mówią metadania, będzie najbardziej wewnętrznym elementem DOM) - i wszystkie jej elementy nadrzędne. Albo dotrzesz do obserwowanego elementu, co oznacza, że użytkownik kliknął w nim, albo do elementu dokumentu, w którym to przypadku kliknął na zewnątrz. Alternatywnie, możesz dodać dwa detektory kliknięć - jeden na elemencie dokumentu, a drugi na obserwowanym elemencie, który używaevent.stopPropagation()
/cancelBubble
tak, że detektor elementu dokumentu jest uruchamiany tylko przez kliknięcie poza obserwowanym elementem.for
. Czy z ciekawości byłby jakiś powód, aby nie porównywać elementu ze zmiennej z celem zdarzenia (przy użyciu ścisłego porównania)?Co powiesz na to:
jsBin demo
document.onclick = function(event){ var hasParent = false; for(var node = event.target; node != document.body; node = node.parentNode) { if(node.id == 'div1'){ hasParent = true; break; } } if(hasParent) alert('inside'); else alert('outside'); }
źródło
Aby ukryć element, klikając poza nim, zwykle stosuję taki prosty kod:
var bodyTag = document.getElementsByTagName('body'); var element = document.getElementById('element'); function clickedOrNot(e) { if (e.target !== element) { // action in the case of click outside bodyTag[0].removeEventListener('click', clickedOrNot, true); } } bodyTag[0].addEventListener('click', clickedOrNot, true);
źródło
Innym bardzo prostym i szybkim podejściem do tego problemu jest odwzorowanie tablicy
path
na obiekt zdarzenia zwracany przez nasłuchiwanie. Jeśli nazwaid
lubclass
elementu pasuje do jednego z elementów w tablicy, kliknięcie znajduje się wewnątrz elementu.(To rozwiązanie może być przydatne, jeśli nie chcesz pobierać elementu bezpośrednio (np .:
document.getElementById('...')
na przykład w aplikacji actjs / nextjs, w ssr ..).Oto przykład:
document.addEventListener('click', e => { let clickedOutside = true; e.path.forEach(item => { if (!clickedOutside) return; if (item.className === 'your-element-class') clickedOutside = false; }); if (clickedOutside) // Make an action if it's clicked outside.. });
Mam nadzieję, że ta odpowiedź Ci pomoże! (Daj mi znać, jeśli moje rozwiązanie nie jest dobrym rozwiązaniem lub jeśli widzisz coś do poprawy).
źródło
Zrobiłem dużo badań, aby znaleźć lepszą metodę. Metoda JavaScript .contains przechodzi rekurencyjnie w DOM, aby sprawdzić, czy zawiera cel, czy nie. Użyłem go w jednym z projektów react, ale kiedy reaguj na zmiany DOM na ustawiony stan, metoda .contains nie działa. Więc wymyśliłem to rozwiązanie
//Basic Html snippet <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> </head> <body> <div id="mydiv"> <h2> click outside this div to test </h2> Check click outside </div> </body> </html> //Implementation in Vanilla javaScript const node = document.getElementById('mydiv') //minor css to make div more obvious node.style.width = '300px' node.style.height = '100px' node.style.background = 'red' let isCursorInside = false //Attach mouseover event listener and update in variable node.addEventListener('mouseover', function() { isCursorInside = true console.log('cursor inside') }) /Attach mouseout event listener and update in variable node.addEventListener('mouseout', function() { isCursorInside = false console.log('cursor outside') }) document.addEventListener('click', function() { //And if isCursorInside = false it means cursor is outside if(!isCursorInside) { alert('Outside div click detected') } })
PRACA DEMO jsfiddle
źródło