Как определить клик вне элемента?
У меня есть некоторые HTML-меню, которые я показываю полностью, когда пользователь нажимает на заголовок этих меню. Я хотел бы скрыть эти элементы, когда пользователь щелкает за пределами области меню.
Что-то вроде этого возможно с jQuery?
$("#menuscontainer").clickOutsideThisElement(function() {
// Hide the menus
});
Ответы
Ответ 1
ПРИМЕЧАНИЕ. Следует избегать использования stopEventPropagation()
, поскольку оно нарушает нормальный поток событий в DOM. Смотрите эту статью для получения дополнительной информации. Попробуйте использовать этот метод вместо
Прикрепите событие щелчка к телу документа, которое закрывает окно. Прикрепите отдельное событие щелчка к контейнеру, который останавливает распространение в теле документа.
$(window).click(function() {
//Hide the menus if visible
});
$('#menucontainer').click(function(event){
event.stopPropagation();
});
Ответ 2
Вы можете прослушать событие click в document
, а затем убедиться, что #menucontainer
не является предком или целью выбранного элемента, используя .closest()
.
Если это не так, то выбранный элемент находится за пределами #menucontainer
, и его можно смело скрывать.
$(document).click(function(event) {
$target = $(event.target);
if(!$target.closest('#menucontainer').length &&
$('#menucontainer').is(":visible")) {
$('#menucontainer').hide();
}
});
Изменить - 2017-06-23
Вы также можете очистить после прослушивателя событий, если вы планируете закрыть меню и хотите прекратить прослушивание событий. Эта функция очистит только недавно созданного прослушивателя, сохранив все остальные прослушиватели щелчков на document
. С синтаксисом ES2015:
export function hideOnClickOutside(selector) {
const outsideClickListener = (event) => {
$target = $(event.target);
if (!$target.closest(selector).length && $(selector).is(':visible')) {
$(selector).hide();
removeClickListener();
}
}
const removeClickListener = () => {
document.removeEventListener('click', outsideClickListener)
}
document.addEventListener('click', outsideClickListener)
}
Изменить - 2018-03-11
Для тех, кто не хочет использовать jQuery. Здесь приведенный выше код в простом vanillaJS (ECMAScript6).
function hideOnClickOutside(element) {
const outsideClickListener = event => {
if (!element.contains(event.target) && isVisible(element)) { // or use: event.target.closest(selector) === null
element.style.display = 'none'
removeClickListener()
}
}
const removeClickListener = () => {
document.removeEventListener('click', outsideClickListener)
}
document.addEventListener('click', outsideClickListener)
}
const isVisible = elem => !!elem && !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ) // source (2018-03-11): https://github.com/jquery/jquery/blob/master/src/css/hiddenVisibleSelectors.js
Примечание:
Это основано на комментариях Алекса, чтобы просто использовать !element.contains(event.target)
вместо части jQuery.
Но element.closest()
теперь также доступен во всех основных браузерах (версия W3C немного отличается от версии jQuery).
Полифилы можно найти здесь: Element.closest()
Ответ 3
Как обнаружить клик вне элемента?
Причина, по которой этот вопрос настолько популярен и имеет так много ответов, состоит в том, что он обманчиво сложный. После почти восьми лет и десятков ответов я искренне удивлен, увидев, как мало внимания уделяется доступности.
Я хотел бы скрыть эти элементы, когда пользователь щелкает за пределами области меню.
Это благородное дело и является актуальной проблемой. Название вопроса - это то, что большинство ответов, по-видимому, пытается решить, содержит несчастную красную селедку.
Подсказка: это слово "click"!
На самом деле вы не хотите привязывать обработчики кликов.
Если вы привязываете обработчики кликов, чтобы закрыть диалоговое окно, вы уже потерпели неудачу. Причина, по которой вы потерпели неудачу, состоит в том, что не каждый вызывает события click
. Пользователи, не использующие мышь, смогут выйти из вашего диалогового окна (и ваше всплывающее меню, возможно, является типом диалога), нажав Tab, и тогда они не смогут прочитать содержимое за диалогом без последующего запуска a click
.
Так что давайте перефразируем вопрос.
Как закрыть диалог, когда пользователь закончил с ним?
Это цель. К сожалению, теперь нам нужно связать событие userisfinishedwiththedialog
, и это связывание не так просто.
Итак, как мы можем обнаружить, что пользователь закончил использование диалога?
focusout
событие
Хорошим началом является определение того, оставил ли фокус диалог.
Подсказка: будьте осторожны с событием blur
, blur
не распространяется, если событие было связано с фазой барботирования!
jQuery focusout
будет прекрасно. Если вы не можете использовать jQuery, вы можете использовать blur
на этапе захвата:
element.addEventListener('blur', ..., true);
// use capture: ^^^^
Кроме того, для многих диалогов вам нужно будет позволить контейнеру получить фокус. Добавьте tabindex="-1"
, чтобы диалоговое окно могло получать фокус динамически, не прерывая при этом поток табуляции.
$('a').on('click', function () {
$(this.hash).toggleClass('active').focus();
});
$('div').on('focusout', function () {
$(this).removeClass('active');
});
div {
display: none;
}
.active {
display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#example">Example</a>
<div id="example" tabindex="-1">
Lorem ipsum <a href="http://example.com">dolor</a> sit amet.
</div>
Ответ 4
Другие решения здесь не работали для меня, поэтому я должен был использовать:
if(!$(event.target).is('#foo'))
{
// hide menu
}
Ответ 5
У меня есть приложение, которое работает аналогично примеру Eran, за исключением того, что я прикрепляю событие click к телу при открытии меню... Kinda вроде этого:
$('#menucontainer').click(function(event) {
$('html').one('click',function() {
// Hide the menus
});
event.stopPropagation();
});
Дополнительная информация о jQuery one()
функция
Ответ 6
$("#menuscontainer").click(function() {
$(this).focus();
});
$("#menuscontainer").blur(function(){
$(this).hide();
});
Работает для меня просто отлично.
Ответ 7
После исследования я нашел три рабочих решения (я забыл ссылки на страницы для справки)
Первое решение
<script>
//The good thing about this solution is it doesn't stop event propagation.
var clickFlag = 0;
$('body').on('click', function () {
if(clickFlag == 0) {
console.log('hide element here');
/* Hide element here */
}
else {
clickFlag=0;
}
});
$('body').on('click','#testDiv', function (event) {
clickFlag = 1;
console.log('showed the element');
/* Show the element */
});
</script>
Второе решение
<script>
$('body').on('click', function(e) {
if($(e.target).closest('#testDiv').length == 0) {
/* Hide dropdown here */
}
});
</script>
Третье решение
<script>
var specifiedElement = document.getElementById('testDiv');
document.addEventListener('click', function(event) {
var isClickInside = specifiedElement.contains(event.target);
if (isClickInside) {
console.log('You clicked inside')
}
else {
console.log('You clicked outside')
}
});
</script>
Ответ 8
Теперь для этого есть плагин: внешние события (сообщение в блоге)
Следующее происходит, когда обработчик clickoutside (WLOG) привязан к элементу:
- элемент добавляется в массив, который содержит все элементы с обработчиками clickoutside.
- a (namespaced) обработчик клика привязан к документу (если он еще не существует)
- при любом щелчке в документе событие clickoutside запускается для тех элементов в этом массиве, которые не равны или родительский объект цели-клика
- Кроме того, event.target для события clickoutside установлен на элемент, на который пользователь нажал (чтобы вы даже знали, что пользователь нажал, а не только, что он щелкнул снаружи).
Таким образом, никакие события не прекращаются из-за распространения, а дополнительные обработчики кликов могут использоваться "выше" с помощью внешнего обработчика.
Ответ 9
Это сработало для меня отлично!
$('html').click(function (e) {
if (e.target.id == 'YOUR-DIV-ID') {
//do something
} else {
//do something
}
});
Ответ 10
Я не думаю, что вам действительно нужно закрыть меню, когда пользователь нажимает на него; вам нужно, чтобы меню закрывалось, когда пользователь нажимает на любом месте на странице. Если вы нажмете на меню или вне меню, он должен закрыть его прямо?
Не найдя удовлетворительных ответов, я попросил написать этот пост в блоге на днях. Для более педантичного, есть несколько ошибок, чтобы принять к сведению:
- Если вы присоедините обработчик события клика к элементу body во время клика, обязательно дождитесь второго щелчка перед закрытием меню и отвяжите событие. В противном случае событие клика, открывшее меню, выйдет на экран слушателя, который должен закрыть меню.
- Если вы используете event.stopPropogation() в событии клика, никакие другие элементы на вашей странице не могут иметь функцию click-where-to-close.
- Прикрепление обработчика события click к элементу body неограниченно не является эффективным решением
- Сравнение цели события и его родителей с создателем обработчика предполагает, что вы хотите закрыть меню при нажатии на него, когда то, что вы действительно хотите, это закрыть его, когда вы нажимаете в любом месте страницы.
- Прослушивание событий на элементе body сделает ваш код более хрупким. Стиль невинно, как это могло бы сломать его:
body { margin-left:auto; margin-right: auto; width:960px;}
Ответ 11
В качестве другого плаката сказано, что есть много ошибок, особенно если элемент, который вы показываете (в данном случае меню), имеет интерактивные элементы.
Я нашел следующий метод достаточно надежным:
$('#menuscontainer').click(function(event) {
//your code that shows the menus fully
//now set up an event listener so that clicking anywhere outside will close the menu
$('html').click(function(event) {
//check up the tree of the click target to check whether user has clicked outside of menu
if ($(event.target).parents('#menuscontainer').length==0) {
// your code to hide menu
//this event listener has done its job so we can unbind it.
$(this).unbind(event);
}
})
});
Ответ 12
Простым решением ситуации является:
$(document).mouseup(function (e)
{
var container = $("YOUR SELECTOR"); // Give you class or ID
if (!container.is(e.target) && // If the target of the click is not the desired div or section
container.has(e.target).length === 0) // ... nor a descendant-child of the container
{
container.hide();
}
});
Выше сценарий будет скрыть div
, если за пределы div
события щелчка срабатывает.
Дополнительную информацию вы можете найти в следующем блоге: http://www.codecanal.com/detect-click-outside-div-using-javascript/
Ответ 13
Solution1
Вместо использования event.stopPropagation(), который может иметь некоторые побочные эффекты, просто определите простую переменную флага и добавьте одно условие if
. Я тестировал это и работал нормально без каких-либо побочных эффектов stopPropagation:
var flag = "1";
$('#menucontainer').click(function(event){
flag = "0"; // flag 0 means click happened in the area where we should not do any action
});
$('html').click(function() {
if(flag != "0"){
// Hide the menus if visible
}
else {
flag = "1";
}
});
Solution2
С помощью простого условия if
:
$(document).on('click', function(event){
var container = $("#menucontainer");
if (!container.is(event.target) && // If the target of the click isn't the container...
container.has(event.target).length === 0) // ... nor a descendant of the container
{
// Do whatever you want to do when click is outside the element
}
});
Ответ 14
Проверьте целевую точку события щелчка окна (он должен распространяться в окне, если он не был захвачен нигде) и убедитесь, что он не является одним из элементов меню. Если это не так, вы находитесь вне своего меню.
Или проверьте положение щелчка и посмотрите, содержится ли он в области меню.
Ответ 15
У меня был успех с чем-то вроде этого:
var $menuscontainer = ...;
$('#trigger').click(function() {
$menuscontainer.show();
$('body').click(function(event) {
var $target = $(event.target);
if ($target.parents('#menuscontainer').length == 0) {
$menuscontainer.hide();
}
});
});
Логика такова: когда показан #menuscontainer
, привяжите обработчик кликов к телу, который скрывает #menuscontainer
только в том случае, если цель (щелчка) не является дочерним элементом.
Ответ 16
В качестве варианта:
var $menu = $('#menucontainer');
$(document).on('click', function (e) {
// If element is opened and click target is outside it, hide it
if ($menu.is(':visible') && !$menu.is(e.target) && !$menu.has(e.target).length) {
$menu.hide();
}
});
У него нет проблем с остановкой распространения событий и лучше поддерживает несколько меню на той же странице, где щелчок по второму меню при первом открытии оставит первое открытие в решении stopPropagation.
Ответ 17
Я нашел этот метод в некотором плагине календаря jQuery.
function ClickOutsideCheck(e)
{
var el = e.target;
var popup = $('.popup:visible')[0];
if (popup==undefined)
return true;
while (true){
if (el == popup ) {
return true;
} else if (el == document) {
$(".popup").hide();
return false;
} else {
el = $(el).parent()[0];
}
}
};
$(document).bind('mousedown.popup', ClickOutsideCheck);
Ответ 18
Событие имеет свойство, называемое event.path элемента, который является "статическим упорядоченным списком всех его предков в порядке дерева". Чтобы проверить, произошло ли событие из определенного элемента DOM или одного из его дочерних элементов, просто проверьте путь к этому конкретному элементу DOM. Он также может использоваться для проверки нескольких элементов логически OR
при проверке элемента в функции some
.
$("body").click(function() {
target = document.getElementById("main");
flag = event.path.some(function(el, i, arr) {
return (el == target)
})
if (flag) {
console.log("Inside")
} else {
console.log("Outside")
}
});
#main {
display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="main">
<ul>
<li>Test-Main</li>
<li>Test-Main</li>
<li>Test-Main</li>
<li>Test-Main</li>
<li>Test-Main</li>
</ul>
</div>
<div id="main2">
Outside Main
</div>
Ответ 19
Вот решение для ванильного JavaScript для будущих зрителей.
При щелчке по любому элементу внутри документа, если щелкнул идентификатор элемента clicked, или скрытый элемент не скрыт, а скрытый элемент не содержит элемент с щелчком, переключите элемент.
(function () {
"use strict";
var hidden = document.getElementById('hidden');
document.addEventListener('click', function (e) {
if (e.target.id == 'toggle' || (hidden.style.display != 'none' && !hidden.contains(e.target))) hidden.style.display = hidden.style.display == 'none' ? 'block' : 'none';
}, false);
})();
(function () {
"use strict";
var hidden = document.getElementById('hidden');
document.addEventListener('click', function (e) {
if (e.target.id == 'toggle' || (hidden.style.display != 'none' && !hidden.contains(e.target))) hidden.style.display = hidden.style.display == 'none' ? 'block' : 'none';
}, false);
})();
<a href="javascript:void(0)" id="toggle">Toggle Hidden Div</a>
<div id="hidden" style="display: none;">This content is normally hidden. click anywhere other than this content to make me disappear</div>
Ответ 20
Я удивлен, что никто на самом деле не признал событие focusout
:
var button = document.getElementById('button');
button.addEventListener('click', function(e){
e.target.style.backgroundColor = 'green';
});
button.addEventListener('focusout', function(e){
e.target.style.backgroundColor = '';
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<button id="button">Click</button>
</body>
</html>
Ответ 21
Если вы используете скрипты для IE и FF 3. *, и вы просто хотите знать, произошел ли щелчок в определенной области окна, вы также можете использовать что-то вроде:
this.outsideElementClick = function(objEvent, objElement){
var objCurrentElement = objEvent.target || objEvent.srcElement;
var blnInsideX = false;
var blnInsideY = false;
if (objCurrentElement.getBoundingClientRect().left >= objElement.getBoundingClientRect().left && objCurrentElement.getBoundingClientRect().right <= objElement.getBoundingClientRect().right)
blnInsideX = true;
if (objCurrentElement.getBoundingClientRect().top >= objElement.getBoundingClientRect().top && objCurrentElement.getBoundingClientRect().bottom <= objElement.getBoundingClientRect().bottom)
blnInsideY = true;
if (blnInsideX && blnInsideY)
return false;
else
return true;}
Ответ 22
Использование:
var go = false;
$(document).click(function(){
if(go){
$('#divID').hide();
go = false;
}
})
$("#divID").mouseover(function(){
go = false;
});
$("#divID").mouseout(function (){
go = true;
});
$("btnID").click( function(){
if($("#divID:visible").length==1)
$("#divID").hide(); // Toggle
$("#divID").show();
});
Ответ 23
Вместо использования прерывания потока, события размытия/фокусировки или любой другой сложной техники просто сопоставьте поток событий с родством элементов:
$(document).on("click.menu-outside", function(event){
// Test if target and it parent aren't #menuscontainer
// That means the click event occur on other branch of document tree
if(!$(event.target).parents().andSelf().is("#menuscontainer")){
// Click outisde #menuscontainer
// Hide the menus (but test if menus aren't already hidden)
}
});
Чтобы удалить прослушиватель внешних событий, просто:
$(document).off("click.menu-outside");
Ответ 24
Подключить к документу прослушиватель событий щелчка. Внутри прослушивателя событий вы можете посмотреть объект события, в частности, event.target, чтобы увидеть, какой элемент был нажат:
$(document).click(function(e){
if ($(e.target).closest("#menuscontainer").length == 0) {
// .closest can help you determine if the element
// or one of its ancestors is #menuscontainer
console.log("hide");
}
});
Ответ 25
$(document).click(function() {
$(".overlay-window").hide();
});
$(".overlay-window").click(function() {
return false;
});
Если вы нажмете на документ, скройте данный элемент, если вы не нажмете на тот же самый элемент.
Ответ 26
Я сделал это так: YUI 3:
// Detect the click anywhere other than the overlay element to close it.
Y.one(document).on('click', function (e) {
if (e.target.ancestor('#overlay') === null && e.target.get('id') != 'show' && overlay.get('visible') == true) {
overlay.hide();
}
});
Я проверяю, не является ли предком не контейнер элементов виджета,
если цель не та, которая открывает виджет/элемент,
если виджет/элемент, который я хочу закрыть, уже открыт (не так важно).
Ответ 27
Перейдите на самый популярный ответ, но добавьте
&& (e.target != $('html').get(0)) // ignore the scrollbar
поэтому щелчок по полосе прокрутки не [скрыть или что-либо еще] вашего целевого элемента.
Ответ 28
Для более удобного использования и более выразительного кода я создал для него плагин jQuery:
$('div.my-element').clickOut(function(target) {
//do something here...
});
Примечание. target - это элемент, который пользователь нажал. Но обратный вызов все еще выполняется в контексте исходного элемента, поэтому вы можете использовать этот, как вы ожидали бы в обратном вызове jQuery.
Plugin:
$.fn.clickOut = function (parent, fn) {
var context = this;
fn = (typeof parent === 'function') ? parent : fn;
parent = (parent instanceof jQuery) ? parent : $(document);
context.each(function () {
var that = this;
parent.on('click', function (e) {
var clicked = $(e.target);
if (!clicked.is(that) && !clicked.parents().is(that)) {
if (typeof fn === 'function') {
fn.call(that, clicked);
}
}
});
});
return context;
};
По умолчанию прослушиватель событий кликов помещается в документ. Однако, если вы хотите ограничить область прослушивателя событий, вы можете передать объект jQuery, представляющий элемент родительского уровня, который будет основным родителем, по которому будут прослушиваться клики. Это предотвращает ненужные прослушиватели событий уровня документа. Очевидно, что это не сработает, если родительский элемент не является родителем вашего начального элемента.
Используйте так:
$('div.my-element').clickOut($('div.my-parent'), function(target) {
//do something here...
});
Ответ 29
Если кому-то интересно, это javascript-решение (es6):
window.addEventListener('mouseup', e => {
if (e.target != yourDiv && e.target.parentNode != yourDiv) {
yourDiv.classList.remove('show-menu');
//or yourDiv.style.display = 'none';
}
})
и es5, на всякий случай:
window.addEventListener('mouseup', function (e) {
if (e.target != yourDiv && e.target.parentNode != yourDiv) {
yourDiv.classList.remove('show-menu');
//or yourDiv.style.display = 'none';
}
});
Ответ 30
Я использовал скрипт ниже и сделал с JQuery.
jQuery(document).click(function(e) {
var target = e.target; //target div recorded
if (!jQuery(target).is('#tobehide') ) {
jQuery(this).fadeOut(); //if the click element is not the above id will hide
}
})
Ниже найдите код HTML
<div class="main-container">
<div> Hello I am the title</div>
<div class="tobehide">I will hide when you click outside of me</div>
</div>
Вы можете прочитать учебник здесь