Я везде искал хорошее решение, но не могу найти того, в котором не используется jQuery .
Есть ли кроссбраузерный, нормальный способ (без странных взломов или легко взломанного кода) для обнаружения щелчка вне элемента (который может иметь или не иметь дочерних элементов)?
javascript
Тибериу-Ионуй Стан
источник
источник
Ответы:
Добавьте прослушиватель событий
document
и используйтеNode.contains()
его, чтобы определить, находится ли цель события (который является самым внутренним элементом, вызывающим щелчок) внутри указанного вами элемента. Работает даже в 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 } });
Показать фрагмент кода
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>
источник
Вам необходимо обработать событие щелчка на уровне документа. В объекте события у вас есть
target
свойство, самый внутренний элемент DOM, на который был выполнен щелчок. С его помощью вы проверяете себя и подходите к его родителям до элемента документа, если один из них является вашим наблюдаемым элементом.См. Пример на 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"; });
Как всегда, из-за addEventListener / attachEvent это несовместимо с кросс-браузерами, но работает вот так.
Щелкается дочерний элемент, когда не event.target, но один из его родителей является наблюдаемым элементом (я просто считаю уровень для этого). У вас также может быть логическая переменная, если элемент найден или нет, чтобы не возвращать обработчик из предложения for. Мой пример ограничивается тем, что обработчик завершает работу только тогда, когда ничего не совпадает.
Добавляя кроссбраузерность, я обычно делаю это так:
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); } };
Это кроссбраузерный код для присоединения прослушивателя / обработчика событий, включая перезапись
this
в IE, в качестве элемента, как это делает jQuery для своих обработчиков событий. Есть много аргументов, чтобы иметь в виду некоторые кусочки jQuery;)источник
target
свойство (которое, как сказано в метаданных, будет самым внутренним элементом DOM) и все его родительские элементы. Либо вы дойдете до наблюдаемого элемента, что означает, что пользователь щелкнул внутри него, либо вы достигнете элемента документа, и в этом случае он / она щелкнет снаружи. В качестве альтернативы вы можете добавить два прослушивателя щелчка - один на элементе документа, а другой на наблюдаемом элементе, который используетevent.stopPropagation()
/,cancelBubble
чтобы прослушиватель на элементе документа запускался только щелчком вне наблюдаемого элемента.for
. Из любопытства, есть ли причина не сравнивать элемент из переменной с целью в событии (с использованием строгого сравнения)?Как насчет этого:
jsBin демонстрация
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'); }
источник
Чтобы скрыть элемент щелчком вне его, я обычно применяю такой простой код:
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);
источник
Еще один очень простой и быстрый подход к этой проблеме - сопоставить массив
path
с объектом события, возвращаемым слушателем. Если имяid
илиclass
вашего элемента совпадает с одним из названий в массиве, щелчок находится внутри вашего элемента.(Это решение может быть полезно, если вы не хотите получать элемент напрямую (например:
document.getElementById('...')
например, в приложении reactjs / nextjs, в ssr ..).Вот пример:
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.. });
Надеюсь, этот ответ вам поможет! (Сообщите мне, если мое решение не является хорошим решением или если вы заметите, что нужно улучшить.)
источник
Я провел много исследований, чтобы найти лучший метод. Метод JavaScript .contains рекурсивно используется в DOM, чтобы проверить, содержит ли он цель или нет. Я использовал его в одном из проектов реагирования, но при реагировании на изменения DOM в установленном состоянии метод .contains не работает. Итак, я придумал это решение
//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') } })
РАБОЧАЯ ДЕМО jsfiddle
источник