Как я могу обнаружить щелчок за пределами элемента?

2052

У меня есть некоторые HTML-меню, которые я показываю полностью, когда пользователь нажимает на заголовок этих меню. Я хотел бы скрыть эти элементы, когда пользователь щелкает за пределами области меню.

Что-то вроде этого возможно с jQuery?

$("#menuscontainer").clickOutsideThisElement(function() {
    // Hide the menus
});
Показать ещё 4 комментария
Теги:

77 ответов

1671
Лучший ответ

ПРИМЕЧАНИЕ. Следует избегать использования stopEventPropagation(), поскольку оно нарушает нормальный поток событий в DOM. Смотрите эту статью для получения дополнительной информации. Попробуйте использовать этот метод вместо

Прикрепите событие щелчка к телу документа, которое закрывает окно. Прикрепите отдельное событие щелчка к контейнеру, который останавливает распространение в теле документа.

$(window).click(function() {
//Hide the menus if visible
});

$('#menucontainer').click(function(event){
    event.stopPropagation();
});
  • 25
    Я предпочитаю привязывать документ к событию click, а затем, когда это необходимо, отменять привязку. это более эффективно.
  • 643
    Это нарушает стандартное поведение многих вещей, включая кнопки и ссылки, содержащиеся в #menucontainer. Я удивлен, что этот ответ так популярен.
Показать ещё 28 комментариев
1272

Вы можете прослушать событие click для document а затем убедиться, что #menucontainer не является предком или целью .closest() элемента с помощью .closest().

Если это не так, то #menucontainer элемент находится за пределами #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). Полифиллы можно найти здесь: https://developer.mozilla.org/en-US/docs/Web/API/Element/closest.

  • 1
    Несмотря на то, что ваш метод работает, ваше утверждение полностью ошибочно. #menucontainer - это нижний уровень в цепочке распространения для всех элементов, которые он содержит, поэтому он не меняет своего поведения. Вы должны попробовать это и убедиться сами.
  • 24
    Я пробовал многие другие ответы, но сработал только этот. Благодарю. В итоге я использовал следующий код: $ (document) .click (function (event) {if ($ (event.target) .closest ('. Window'). Length == 0) {$ ('. Window' ) .fadeOut ('fast');}});
Показать ещё 27 комментариев
220

Как обнаружить клик вне элемента?

Причина, по которой этот вопрос настолько популярен и имеет так много ответов, состоит в том, что он обманчиво сложный. После почти восьми лет и десятков ответов я искренне удивлен, увидев, как мало внимания уделяется доступности.

Я хотел бы скрыть эти элементы, когда пользователь щелкает за пределами области меню.

Это благородное дело и является актуальной проблемой. Название вопроса - это то, что большинство ответов, по-видимому, пытается решить, содержит несчастную красную селедку.

Подсказка: это слово "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="/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>

Если вы играете с этой демонстрацией более минуты, вы должны быстро начать просматривать проблемы.

Во-первых, ссылка в диалоговом окне не доступна. Попытка щелкнуть по нему или вкладку к ней приведет к закрытию диалогового окна до того, как произойдет взаимодействие. Это связано с тем, что фокусировка внутреннего элемента вызывает событие focusout перед тем, как снова запустить событие focusin.

Исправление - очередь на изменение состояния в цикле событий. Это можно сделать, используя setImmediate(...) или setTimeout(..., 0) для браузеров, которые не поддерживают setImmediate. После того, как он поставлен в очередь, он может быть отменен последующим focusin:

$('.submenu').on({
  focusout: function (e) {
    $(this).data('submenuTimer', setTimeout(function () {
      $(this).removeClass('submenu--active');
    }.bind(this), 0));
  },
  focusin: function (e) {
    clearTimeout($(this).data('submenuTimer'));
  }
});

$('a').on('click', function () {
  $(this.hash).toggleClass('active').focus();
});

$('div').on({
  focusout: function () {
    $(this).data('timer', setTimeout(function () {
      $(this).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this).data('timer'));
  }
});
div {
  display: none;
}
.active {
  display: block;
}
<script src="/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>

Вторая проблема заключается в том, что диалог не будет закрываться при повторном нажатии ссылки. Это связано с тем, что диалог теряет фокус, вызывая срабатывание по закрытию, после чего щелчок по ссылке вызывает открытие диалогового окна.

Как и в предыдущем выпуске, нужно управлять состоянием фокусировки. Учитывая, что изменение состояния уже поставлено в очередь, это всего лишь вопрос обработки фокусных событий в диалоговом режиме:

Это должно выглядеть знакомо
$('a').on({
  focusout: function () {
    $(this.hash).data('timer', setTimeout(function () {
      $(this.hash).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this.hash).data('timer'));  
  }
});

$('a').on('click', function () {
  $(this.hash).toggleClass('active').focus();
});

$('div').on({
  focusout: function () {
    $(this).data('timer', setTimeout(function () {
      $(this).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this).data('timer'));
  }
});

$('a').on({
  focusout: function () {
    $(this.hash).data('timer', setTimeout(function () {
      $(this.hash).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this.hash).data('timer'));  
  }
});
div {
  display: none;
}
.active {
  display: block;
}
<script src="/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>

Esc ключ

Если вы считаете, что все сделано, управляя состояниями фокусировки, вы можете сделать больше, чтобы упростить работу с пользователем.

Это часто бывает "приятно иметь", но обычно бывает, что когда у вас есть модальный или всплывающий файл любого типа, который закрывает его клавиша Esc.

keydown: function (e) {
  if (e.which === 27) {
    $(this).removeClass('active');
    e.preventDefault();
  }
}

$('a').on('click', function () {
  $(this.hash).toggleClass('active').focus();
});

$('div').on({
  focusout: function () {
    $(this).data('timer', setTimeout(function () {
      $(this).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this).data('timer'));
  },
  keydown: function (e) {
    if (e.which === 27) {
      $(this).removeClass('active');
      e.preventDefault();
    }
  }
});

$('a').on({
  focusout: function () {
    $(this.hash).data('timer', setTimeout(function () {
      $(this.hash).removeClass('active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this.hash).data('timer'));  
  }
});
div {
  display: none;
}
.active {
  display: block;
}
<script src="/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>

Если вы знаете, что в диалоговом окне есть настраиваемые элементы, вам не нужно напрямую сфокусировать диалог. Если вы создаете меню, вы можете сфокусировать первый пункт меню.

click: function (e) {
  $(this.hash)
    .toggleClass('submenu--active')
    .find('a:first')
    .focus();
  e.preventDefault();
}

$('.menu__link').on({
  click: function (e) {
    $(this.hash)
      .toggleClass('submenu--active')
      .find('a:first')
      .focus();
    e.preventDefault();
  },
  focusout: function () {
    $(this.hash).data('submenuTimer', setTimeout(function () {
      $(this.hash).removeClass('submenu--active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this.hash).data('submenuTimer'));  
  }
});

$('.submenu').on({
  focusout: function () {
    $(this).data('submenuTimer', setTimeout(function () {
      $(this).removeClass('submenu--active');
    }.bind(this), 0));
  },
  focusin: function () {
    clearTimeout($(this).data('submenuTimer'));
  },
  keydown: function (e) {
    if (e.which === 27) {
      $(this).removeClass('submenu--active');
      e.preventDefault();
    }
  }
});
.menu {
  list-style: none;
  margin: 0;
  padding: 0;
}
.menu:after {
  clear: both;
  content: '';
  display: table;
}
.menu__item {
  float: left;
  position: relative;
}

.menu__link {
  background-color: lightblue;
  color: black;
  display: block;
  padding: 0.5em 1em;
  text-decoration: none;
}
.menu__link:hover,
.menu__link:focus {
  background-color: black;
  color: lightblue;
}

.submenu {
  border: 1px solid black;
  display: none;
  left: 0;
  list-style: none;
  margin: 0;
  padding: 0;
  position: absolute;
  top: 100%;
}
.submenu--active {
  display: block;
}

.submenu__item {
  width: 150px;
}

.submenu__link {
  background-color: lightblue;
  color: black;
  display: block;
  padding: 0.5em 1em;
  text-decoration: none;
}

.submenu__link:hover,
.submenu__link:focus {
  background-color: black;
  color: lightblue;
}
<script src="/jquery.min.js"></script>
<ul class="menu">
  <li class="menu__item">
    <a class="menu__link" href="#menu-1">Menu 1</a>
    <ul class="submenu" id="menu-1" tabindex="-1">
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#1">Example 1</a></li>
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#2">Example 2</a></li>
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#3">Example 3</a></li>
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#4">Example 4</a></li>
    </ul>
  </li>
  <li class="menu__item">
    <a  class="menu__link" href="#menu-2">Menu 2</a>
    <ul class="submenu" id="menu-2" tabindex="-1">
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#1">Example 1</a></li>
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#2">Example 2</a></li>
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#3">Example 3</a></li>
      <li class="submenu__item"><a class="submenu__link" href="http://example.com/#4">Example 4</a></li>
    </ul>
  </li>
</ul>
lorem ipsum <a href="http://example.com/">dolor</a> sit amet.

Роли WAI-ARIA и другая поддержка доступности

Этот ответ, надеюсь, охватывает основы доступной поддержки клавиатуры и мыши для этой функции, но поскольку он уже довольно значителен, я собираюсь избегать обсуждения ролей и атрибутов WAI-ARIA, однако я настоятельно рекомендую, чтобы разработчики ссылались на спецификацию для подробностей о том, какие роли они должны использовать, и любых других соответствующих атрибутах.

  • 23
    Это самый полный ответ, с объяснениями и доступностью. Я думаю, что это должен быть принятый ответ, так как большинство других ответов обрабатывают только клики и просто отбрасывают фрагмент кода без каких-либо объяснений.
  • 0
    @Cyrille, спасибо за добрые слова. Вам придется убедить ОП изменить принятый ответ, если вы чувствуете себя так сильно.
Показать ещё 13 комментариев
138

Другие решения здесь не работали для меня, поэтому я должен был использовать:

if(!$(event.target).is('#foo'))
{
    // hide menu
}
  • 0
    Я опубликовал еще один практический пример того, как использовать event.target, чтобы не вызывать другие обработчики HTML внешнего виджета Jquery UI при внедрении их в собственное всплывающее окно: Лучший способ получить оригинальную цель
  • 42
    Это сработало для меня, за исключением того, что я добавил && !$(event.target).parents("#foo").is("#foo") внутри оператора IF чтобы любые дочерние элементы не закрывали меню при нажатии.
Показать ещё 6 комментариев
122

У меня есть приложение, которое работает аналогично примеру Eran, за исключением того, что я прикрепляю событие click к телу при открытии меню... Kinda вроде этого:

$('#menucontainer').click(function(event) {
  $('html').one('click',function() {
    // Hide the menus
  });

  event.stopPropagation();
});

Дополнительная информация о jQuery one() функция

  • 9
    но потом, если вы нажмете на само меню, то снаружи оно не будет работать :)
  • 3
    Это помогает поместить event.stopProgagantion () перед привязкой слушателя клика к телу.
Показать ещё 4 комментария
37
$("#menuscontainer").click(function() {
    $(this).focus();
});
$("#menuscontainer").blur(function(){
    $(this).hide();
});

Работает для меня просто отлично.

  • 2
    Это тот, который я бы использовал. Возможно, он не идеален, но как программист-хобби его достаточно просто понять.
  • 0
    размытие - это выход за пределы #menucontainer вопрос был о щелчке
Показать ещё 3 комментария
34

Теперь для этого есть плагин: внешние события (сообщение в блоге)

Следующее происходит, когда обработчик clickoutside (WLOG) привязан к элементу:

  • элемент добавляется в массив, который содержит все элементы с обработчиками clickoutside.
  • a (namespaced) обработчик клика привязан к документу (если он еще не существует)
  • при любом щелчке в документе событие clickoutside запускается для тех элементов в этом массиве, которые не равны или родительский объект цели-клика
  • Кроме того, event.target для события clickoutside установлен на элемент, на который пользователь нажал (чтобы вы даже знали, что пользователь нажал, а не только, что он щелкнул снаружи).

Таким образом, никакие события не прекращаются из-за распространения, а дополнительные обработчики кликов могут использоваться "выше" с помощью внешнего обработчика.

  • 0
    Хороший плагин. Ссылка на «внешние события» неактивна, а ссылка на пост в блоге жива и предоставляет очень полезный плагин для событий типа «clickoutside». Это также MIT по лицензии.
  • 0
    Отличный плагин. Работал отлично. Использование выглядит так: $( '#element' ).on( 'clickoutside', function( e ) { .. } );
33

После исследования я нашел три рабочих решения (я забыл ссылки на страницы для справки)

Первое решение

<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>
  • 7
    Третье решение - безусловно, самый элегантный способ проверки. Это также не связано с накладными расходами jQuery. Очень хорошо. Это очень помогло. Благодарю.
  • 0
    Я пытаюсь получить третье решение с несколькими элементами document.getElementsByClassName , если у кого-то есть подсказка, пожалуйста, поделитесь.
Показать ещё 3 комментария
30

Это сработало для меня отлично!

$('html').click(function (e) {
    if (e.target.id == 'YOUR-DIV-ID') {
        //do something
    } else {
        //do something
    }
});
  • 0
    Это решение хорошо сработало для того, что я делал, где мне все еще нужно было всплывающее событие, спасибо.
25

Я не думаю, что вам действительно нужно закрыть меню, когда пользователь нажимает на него; вам нужно, чтобы меню закрывалось, когда пользователь нажимает на любом месте на странице. Если вы нажмете на меню или вне меню, он должен закрыть его прямо?

Не найдя удовлетворительных ответов, я попросил написать этот пост в блоге на днях. Для более педантичного, есть несколько ошибок, чтобы принять к сведению:

  • Если вы присоедините обработчик события клика к элементу body во время клика, обязательно дождитесь второго щелчка перед закрытием меню и отвяжите событие. В противном случае событие клика, открывшее меню, выйдет на экран слушателя, который должен закрыть меню.
  • Если вы используете event.stopPropogation() в событии клика, никакие другие элементы на вашей странице не могут иметь функцию click-where-to-close.
  • Прикрепление обработчика события click к элементу body неограниченно не является эффективным решением
  • Сравнение цели события и его родителей с создателем обработчика предполагает, что вы хотите закрыть меню при нажатии на него, когда то, что вы действительно хотите, это закрыть его, когда вы нажимаете в любом месте страницы.
  • Прослушивание событий на элементе body сделает ваш код более хрупким. Стиль невинно, как это могло бы сломать его: body { margin-left:auto; margin-right: auto; width:960px;}
  • 1
    «Если вы щелкнете по меню или выйдете из меню, оно должно закрыться, верно?» не всегда. Отмена щелчка путем перетаскивания элемента все равно будет вызывать щелчок на уровне документа, но намерение не будет состоять в том, чтобы продолжать закрывать меню. Существует также множество других типов диалогов, в которых можно использовать поведение «щелчок», которое позволяет осуществлять внутренние щелчки.
24

В качестве другого плаката сказано, что есть много ошибок, особенно если элемент, который вы показываете (в данном случае меню), имеет интерактивные элементы. Я нашел следующий метод достаточно надежным:

$('#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);
        }

    })
});
20

Простым решением ситуации является:

$(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/

18

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
    }
});
  • 0
    Я использовал это решение с логическим флагом, и это хорошо также с сочлененным DOm, а также, если внутри #menucontainer много других элементов
18

Проверьте целевую точку события щелчка окна (он должен распространяться в окне, если он не был захвачен нигде) и убедитесь, что он не является одним из элементов меню. Если это не так, вы находитесь вне своего меню.

Или проверьте положение щелчка и посмотрите, содержится ли он в области меню.

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 только в том случае, если цель (щелчка) не является дочерним элементом.

14

В качестве варианта:

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.

12

Я нашел этот метод в некотором плагине календаря 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);
10

Вот решение для ванильного 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>

Если вы собираетесь иметь несколько переключателей на одной странице, вы можете использовать что-то вроде этого:

  • Добавить имя класса hidden в складной элемент.
  • При щелчке документа закройте все скрытые элементы, которые не содержат элемент с кликом и не скрыты.
  • Если щелкнутый элемент является переключателем, переключите указанный элемент.

(function () {
    "use strict";
    var hiddenItems = document.getElementsByClassName('hidden'), hidden;
    document.addEventListener('click', function (e) {
        for (var i = 0; hidden = hiddenItems[i]; i++) {
            if (!hidden.contains(e.target) && hidden.style.display != 'none')
                hidden.style.display = 'none';
        }
        if (e.target.getAttribute('data-toggle')) {
            var toggle = document.querySelector(e.target.getAttribute('data-toggle'));
            toggle.style.display = toggle.style.display == 'none' ? 'block' : 'none';
        }
    }, false);
})();
<a href="javascript:void(0)" data-toggle="#hidden1">Toggle Hidden Div</a>
<div class="hidden" id="hidden1" style="display: none;" data-hidden="true">This content is normally hidden</div>
<a href="javascript:void(0)" data-toggle="#hidden2">Toggle Hidden Div</a>
<div class="hidden" id="hidden2" style="display: none;" data-hidden="true">This content is normally hidden</div>
<a href="javascript:void(0)" data-toggle="#hidden3">Toggle Hidden Div</a>
<div class="hidden" id="hidden3" style="display: none;" data-hidden="true">This content is normally hidden</div>
9

Событие имеет свойство, называемое 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="/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>

Итак, для вашего случая Это должно быть

$("body").click(function() {
  target = $("#menuscontainer")[0];
  flag = event.path.some(function(el, i, arr) {
    return (el == target)
  });
  if (!flag) {
    // Hide the menus
  }
});
8

Вместо использования прерывания потока, события размытия/фокусировки или любой другой сложной техники просто сопоставьте поток событий с родством элементов:

$(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");
  • 0
    Для меня это было лучшее решение. Использовал его в обратном вызове после анимации, поэтому мне понадобилось событие отсоединения. +1
  • 0
    Здесь есть избыточная проверка. если элемент действительно #menuscontainer вы все равно проходите через его родителей. Вы должны сначала проверить это, и если это не тот элемент, тогда поднимитесь по дереву DOM.
Показать ещё 1 комментарий
8

Если вы используете скрипты для 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;}
7

Использование:

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();
});
6

Подключить к документу прослушиватель событий щелчка. Внутри прослушивателя событий вы можете посмотреть объект события, в частности, 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");
    }
});
5

Я удивлен, что никто на самом деле не признал событие 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>
5

Для более удобного использования и более выразительного кода я создал для него плагин 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...
});
5

Перейдите на самый популярный ответ, но добавьте

&& (e.target != $('html').get(0)) // ignore the scrollbar

поэтому щелчок по полосе прокрутки не [скрыть или что-либо еще] вашего целевого элемента.

5

Я сделал это так: 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();
    }
});

Я проверяю, не является ли предком не контейнер элементов виджета,
если цель не та, которая открывает виджет/элемент,
если виджет/элемент, который я хочу закрыть, уже открыт (не так важно).

5
$(document).click(function() {
    $(".overlay-window").hide();
});
$(".overlay-window").click(function() {
    return false;
});

Если вы нажмете на документ, скройте данный элемент, если вы не нажмете на тот же самый элемент.

4

Я использовал ниже сценарий и сделал с 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>

Вы можете прочитать учебник здесь

4

Мы внедрили решение, частично основанное на комментарии пользователя выше, который отлично работает для нас. Мы используем его, чтобы скрыть окно поиска/результаты при нажатии вне этих элементов, исключая исходный элемент.

// HIDE SEARCH BOX IF CLICKING OUTSIDE
$(document).click(function(event){ 
    // IF NOT CLICKING THE SEARCH BOX OR ITS CONTENTS OR SEARCH ICON 
    if ($("#search-holder").is(":visible") && !$(event.target).is("#search-holder *, #search")) {
        $("#search-holder").fadeOut('fast');
        $("#search").removeClass('active');
    }
});

Он проверяет, действительно ли окно поиска уже видимо и в нашем случае оно также удаляет активный класс в кнопке поиска/поиска.

4

В итоге я сделал что-то вроде этого:

$(document).on('click', 'body, #msg_count_results .close',function() {
    $(document).find('#msg_count_results').remove();
});
$(document).on('click','#msg_count_results',function(e) {
    e.preventDefault();
    return false;
});

У меня есть кнопка закрытия в новом контейнере для целей пользовательского интерфейса, ориентированного на конечных пользователей. Мне пришлось использовать return false, чтобы не пройти. Конечно, наличие A HREF там, где можно взять вас где-нибудь, было бы неплохо, иначе вы могли бы назвать некоторые вещи ajax. В любом случае, он работает нормально для меня. Только то, что я хотел.

4

Это должно работать:

$('body').click(function (event) {
    var obj = $(event.target);
    obj = obj['context']; // context : clicked element inside body
    if ($(obj).attr('id') != "menuscontainer" && $('#menuscontainer').is(':visible') == true) {
        //hide menu
    }
});
  • 0
    $ ("body> div: not (#dvid)"). click (function (e) {// ваш код});
4

Функции:

$(function() {
    $.fn.click_inout = function(clickin_handler, clickout_handler) {
        var item = this;
        var is_me = false;
        item.click(function(event) {
            clickin_handler(event);
            is_me = true;
        });
        $(document).click(function(event) {
            if (is_me) {
                is_me = false;
            } else {
                clickout_handler(event);
            }
        });
        return this;
    }
});

Использование:

this.input = $('<input>')
    .click_inout(
        function(event) { me.ShowTree(event); },
        function() { me.Hide(); }
    )
    .appendTo(this.node);

И функции очень просты:

ShowTree: function(event) {
    this.data_span.show();
}
Hide: function() {
    this.data_span.hide();
}
  • 0
    Не будет ли это вызывать событие clickout также в случае, когда щелкает дочерний элемент контейнера?
3

Вот простое решение с помощью чистого javascript. Он обновлен с ES6:

var isMenuClick = false;
var menu = document.getElementById('menuscontainer');
document.addEventListener('click',()=>{
    if(!isMenuClick){
       //Hide the menu here
    }
    //Reset isMenuClick 
    isMenuClick = false;
})
menu.addEventListener('click',()=>{
    isMenuClick = true;
})
  • 0
    «Современное с ES6» - довольно смелое утверждение, когда единственное, что актуально с ES6, это делать () => {} вместо function() {} . То, что у вас есть, классифицируется как простой JavaScript с изюминкой ES6.
  • 0
    @MortenMoulder: Я. Это просто для внимания, хотя на самом деле это ES6. Но просто посмотрите на решение. Я думаю, что это хорошо.
3

Вот что я делаю, чтобы решить проблему.

$(window).click(function (event) {
    //To improve performance add a checklike 
    //if(myElement.isClosed) return;
    var isClickedElementChildOfMyBox = isChildOfElement(event,'#id-of-my-element');

    if (isClickedElementChildOfMyBox)
        return;

    //your code to hide the element 
});

var isChildOfElement = function (event, selector) {
    if (event.originalEvent.path) {
        return event.originalEvent.path[0].closest(selector) !== null;
    }

    return event.originalEvent.originalTarget.closest(selector) !== null;
}
3

Если кому-то интересно, это 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';
}

});

3

Решения здесь работают отлично , когда нужно управлять одним элементом. Однако, если есть несколько элементов, проблема намного сложнее. Трюки с e.stopPropagation() и всеми остальными не будут работать.

Я придумал решение, и, возможно, это не так просто, но это лучше, чем ничего. Посмотрите:

$view.on("click", function(e) {

    if(model.isActivated()) return;

        var watchUnclick = function() {
            rootView.one("mouseleave", function() {
                $(document).one("click", function() {
                    model.deactivate();
                });
                rootView.one("mouseenter", function() {
                    watchUnclick();
                });
            });
        };
        watchUnclick();
        model.activate();
    });
  • 0
    Я думаю, что onmouseenter и onmouseleave - это решение, проверьте это jsfiddle.net/1r73jm8m
2

Я просто хочу, чтобы @Pistos отвечал более очевидным, так как он скрыт в комментариях.

Это решение отлично работало для меня. Обычный JS:

var elementToToggle = $('.some-element');
$(document).click( function(event) {
  if( $(event.target).closest(elementToToggle).length === 0 ) {
    elementToToggle.hide();
  }
});

в CoffeeScript:

elementToToggle = $('.some-element')
$(document).click (event) ->
  if $(event.target).closest(elementToToggle).length == 0
    elementToToggle.hide()
2

Если вы используете такие инструменты, как "Всплывающее окно", вы можете использовать событие "onFocusOut".

window.onload=function(){
document.getElementById("inside-div").focus();
}
function loseFocus(){
alert("Clicked outside");
}
#container{
background-color:lightblue;
width:200px;
height:200px;
}

#inside-div{
background-color:lightgray;
width:100px;
height:100px;

}
<div id="container">
<input type="text" id="inside-div" onfocusout="loseFocus()">
</div>
2

Это работает для меня

$("body").mouseup(function(e) {
    var subject = $(".main-menu");
    if(e.target.id != subject.attr('id') && !subject.has(e.target).length) {
        $('.sub-menu').hide();
    }
});
2
$(document).on("click",function (event)   
 {   
     console.log(event);
   if ($(event.target).closest('.element').length == 0)
     {
    //your code here
      if ($(".element").hasClass("active"))
      {
        $(".element").removeClass("active");
      }
     }
 });

Попробуйте эту кодировку для получения решения.

2

Я знаю, что есть миллион ответов на этот вопрос, но я всегда был поклонником использования HTML и CSS для большей части работы. В этом случае z-индекс и позиционирование. Самый простой способ, который я нашел для этого, заключается в следующем:

$("#show-trigger").click(function(){
  $("#element").animate({width: 'toggle'});
  $("#outside-element").show();
});
$("#outside-element").click(function(){
  $("#element").hide();
  $("#outside-element").hide();
});
#outside-element {
  position:fixed;
  width:100%;
  height:100%;
  z-index:1;
  display:none;
}
#element {
  display:none;
  padding:20px;
  background-color:#ccc;
  width:300px;
  z-index:2;
  position:relative;
}
#show-trigger {
  padding:20px;
  background-color:#ccc;
  margin:20px auto;
  z-index:2;
}
<script src="/jquery.min.js"></script>
<div id="outside-element"></div>
<div id="element">
  <div class="menu-item"><a href="#1">Menu Item 1</a></div>
  <div class="menu-item"><a href="#2">Menu Item 1</a></div>
  <div class="menu-item"><a href="#3">Menu Item 1</a></div>
  <div class="menu-item"><a href="#4">Menu Item 1</a></div>
</div>
<div id="show-trigger">Show Menu</div>

Это создает безопасную среду, так как ничего не запускается, если только меню не открыто, а z-index защищает любой контент внутри элемента от создания каких-либо пропусков пропуска при нажатии.

Кроме того, вам не нужно, чтобы jQuery охватывал все ваши базы с помощью вызовов распространения и должен был очистить все внутренние элементы от пропусков пропуска.

2

Для сенсорных устройств, таких как iPad и iPhone, мы можем использовать этот код:

$(document).on('touchstart', function (event) {
    var container = $("YOUR CONTAINER SELECTOR");

    if (!container.is(e.target) &&            // If the target of the click isn't the container...
        container.has(e.target).length === 0) // ... nor a descendant of the container
    {
        container.hide();
    }
});
2

Ответ, отмеченный как принятый ответ, не учитывает, что вы можете иметь наложения над элементом, например, диалоги, popovers, datepickers и т.д. Клики в них не должны скрывать элемент.

Я сделал свою собственную версию, которая учитывает это. Он создан как привязка KnockoutJS, но его можно легко преобразовать в jQuery-only.

Он работает по первому запросу для всех элементов, имеющих либо z-индекс, либо абсолютную позицию, которые видны. Затем он проверяет эти элементы на элемент, который я хочу скрыть, если вы нажимаете на него. Если это удар, я вычисляю новый связанный прямоугольник, который учитывает границы наложения.

ko.bindingHandlers.clickedIn = (function () {
    function getBounds(element) {
        var pos = element.offset();
        return {
            x: pos.left,
            x2: pos.left + element.outerWidth(),
            y: pos.top,
            y2: pos.top + element.outerHeight()
        };
    }

    function hitTest(o, l) {
        function getOffset(o) {
            for (var r = { l: o.offsetLeft, t: o.offsetTop, r: o.offsetWidth, b: o.offsetHeight };
                o = o.offsetParent; r.l += o.offsetLeft, r.t += o.offsetTop);
            return r.r += r.l, r.b += r.t, r;
        }

        for (var b, s, r = [], a = getOffset(o), j = isNaN(l.length), i = (j ? l = [l] : l).length; i;
            b = getOffset(l[--i]), (a.l == b.l || (a.l > b.l ? a.l <= b.r : b.l <= a.r))
                && (a.t == b.t || (a.t > b.t ? a.t <= b.b : b.t <= a.b)) && (r[r.length] = l[i]));
        return j ? !!r.length : r;
    }

    return {
        init: function (element, valueAccessor) {
            var target = valueAccessor();
            $(document).click(function (e) {
                if (element._clickedInElementShowing === false && target()) {
                    var $element = $(element);
                    var bounds = getBounds($element);

                    var possibleOverlays = $("[style*=z-index],[style*=absolute]").not(":hidden");
                    $.each(possibleOverlays, function () {
                        if (hitTest(element, this)) {
                            var b = getBounds($(this));
                            bounds.x = Math.min(bounds.x, b.x);
                            bounds.x2 = Math.max(bounds.x2, b.x2);
                            bounds.y = Math.min(bounds.y, b.y);
                            bounds.y2 = Math.max(bounds.y2, b.y2);
                        }
                    });

                    if (e.clientX < bounds.x || e.clientX > bounds.x2 ||
                        e.clientY < bounds.y || e.clientY > bounds.y2) {

                        target(false);
                    }
                }
                element._clickedInElementShowing = false;
            });

            $(element).click(function (e) {
                e.stopPropagation();
            });
        },
        update: function (element, valueAccessor) {
            var showing = ko.utils.unwrapObservable(valueAccessor());
            if (showing) {
                element._clickedInElementShowing = true;
            }
        }
    };
})();
2

Самый простой способ сделать это - выбрать все на веб-странице, кроме элемента, где вы не хотите, чтобы обнаруженные клики и привязывали событие клика к ним при открытии меню.

Затем, когда меню закрыто, удалите привязку.

Используйте .stopPropagation, чтобы предотвратить случайное воздействие какой-либо части меню.

$("*").not($("#menuscontainer")).bind("click.OutsideMenus", function ()
{
    // hide the menus

    //then remove all of the handlers
    $("*").unbind(".OutsideMenus");
});

$("#menuscontainer").bind("click.OutsideMenus", function (event) 
{
    event.stopPropagation(); 
});
2

Еще одно решение:

http://jsfiddle.net/zR76D/

Использование:

<div onClick="$('#menu').toggle();$('#menu').clickOutside(function() { $(this).hide(); $(this).clickOutside('disable'); });">Open / Close Menu</div>
<div id="menu" style="display: none; border: 1px solid #000000; background: #660000;">I am a menu, whoa is me.</div>

Plugin:

(function($) {
    var clickOutsideElements = [];
    var clickListener = false;

    $.fn.clickOutside = function(options, ignoreFirstClick) {
        var that = this;
        if (ignoreFirstClick == null) ignoreFirstClick = true;

        if (options != "disable") {
            for (var i in clickOutsideElements) {
                if (clickOutsideElements[i].element[0] == $(this)[0]) return this;
            }

            clickOutsideElements.push({ element : this, clickDetected : ignoreFirstClick, fnc : (typeof(options) != "function") ? function() {} : options });

            $(this).on("click.clickOutside", function(event) {
                for (var i in clickOutsideElements) {
                    if (clickOutsideElements[i].element[0] == $(this)[0]) {
                        clickOutsideElements[i].clickDetected = true;
                    }
                }
            });

            if (!clickListener) {
                if (options != null && typeof(options) == "function") {
                    $('html').click(function() {
                        for (var i in clickOutsideElements) {
                            if (!clickOutsideElements[i].clickDetected) {
                                clickOutsideElements[i].fnc.call(that);
                            }
                            if (clickOutsideElements[i] != null) clickOutsideElements[i].clickDetected = false;
                        }
                    });
                    clickListener = true;
                }
            }
        }
        else {
            $(this).off("click.clickoutside");
            for (var i = 0; i < clickOutsideElements.length; ++i) {
                if (clickOutsideElements[i].element[0] == $(this)[0]) {
                    clickOutsideElements.splice(i, 1);
                }
            }
        }

        return this;
    }
})(jQuery);
2

Честно говоря, мне не понравилось ни одно из предыдущих решений.

Лучший способ сделать это - привязать событие "click" к документу и сравнить, действительно ли этот клик находится вне элемента (так же, как Art сказал в своем предложении).

Однако у вас будут проблемы: вы никогда не сможете отвязать его, и у вас нет внешней кнопки для открытия/закрытия этого элемента.

Вот почему я написал этот небольшой плагин (нажмите здесь, чтобы ссылку), чтобы упростить эти задачи. Это может быть проще?

<a id='theButton' href="#">Toggle the menu</a><br/>
<div id='theMenu'>
    I should be toggled when the above menu is clicked,
    and hidden when user clicks outside.
</div>

<script>
$('#theButton').click(function(){
    $('#theMenu').slideDown();
});
$("#theMenu").dClickOutside({ ignoreList: $("#theButton") }, function(clickedObj){
    $(this).slideUp();
});
</script>
2
jQuery().ready(function(){
    $('#nav').click(function (event) {
        $(this).addClass('activ');
        event.stopPropagation();
    });

    $('html').click(function () {
        if( $('#nav').hasClass('activ') ){
            $('#nav').removeClass('activ');
        }
    });
});
2

Вот мой код:

// Listen to every click
$('html').click(function(event) {
    if ( $('#mypopupmenu').is(':visible') ) {
        if (event.target.id != 'click_this_to_show_mypopupmenu') {
            $('#mypopupmenu').hide();
        }
    }
});

// Listen to selector clicks
$('#click_this_to_show_mypopupmenu').click(function() {

  // If the menu is visible, and you clicked the selector again we need to hide
  if ( $('#mypopupmenu').is(':visible') {
      $('#mypopupmenu').hide();
      return true;
  }

  // Else we need to show the popup menu
  $('#mypopupmenu').show();
});
2

Это сработало отлично для меня:

$('body').click(function() {
    // Hide the menus if visible.
});
  • 2
    проблема в том, что независимо от того, где вы щелкаете, событие всегда будет направлено вниз к телу. так что вы должны использовать event.stopPropagation, но это нарушит этот естественный способ поведения DOM. Я не думаю, что это хорошая практика.
1
$('#propertyType').on("click",function(e){
          self.propertyTypeDialog = !self.propertyTypeDialog;
          b = true;
          e.stopPropagation();
          console.log("input clicked");
      });

      $(document).on('click','body:not(#propertyType)',function (e) {
          e.stopPropagation();
          if(b == true)  {
              if ($(e.target).closest("#configuration").length == 0) {
                  b = false;
                  self.propertyTypeDialog = false;
                  console.log("outside clicked");
              }
          }
        // console.log($(e.target).closest("#configuration").length);
      });
1

Я считаю, что лучший способ сделать это - вот что.

$(document).on("click", function(event) {
  clickedtarget = $(event.target).closest('#menuscontainer');
  $("#menuscontainer").not(clickedtarget).hide();
});

Этот тип решения может быть легко выполнен для работы в нескольких меню, а также в меню, которые динамически добавляются через javascript. В основном это просто позволяет вам щелкнуть в любом месте документа и проверяет, на какой элемент вы нажали, и выбирает ближайший "#menuscontainer". Затем он скрывает все меню, но исключает тот, который вы нажали.

Не уверен точно, как вы создаете свои меню, но не стесняйтесь копировать мой код в JSFiddle. Это очень простая, но функционально функциональная система меню/мода. Все, что вам нужно сделать, это создать html-меню, и код будет работать для вас.

https://jsfiddle.net/zs6anrn7/

1

Это может быть лучшим решением для некоторых людей.

$(".menu_link").click(function(){
    // show menu code
});

$(".menu_link").mouseleave(function(){
    //hide menu code, you may add a timer for 3 seconds before code to be run
});

Я знаю, что mouseleave означает не только щелчок снаружи, но и выход из этой области элементов.

Как только само меню находится внутри элемента menu_link, тогда само меню не должно быть проблемой, чтобы нажимать или перемещаться.

  • 0
    Mouseleave и какой-то взлом может решить эту проблему для некоторых людей, вот тест jsfiddle.net/1r73jm8m
1

Простой плагин:

$.fn.clickOff = function(callback, selfDestroy) {
    var clicked = false;
    var parent = this;
    var destroy = selfDestroy || true;

    parent.click(function() {
        clicked = true;
    });

    $(document).click(function(event) {
        if (!clicked && parent.is(':visible')) {
            if(callback) callback.call(parent, event)
        }
        if (destroy) {
            //parent.clickOff = function() {};
            //parent.off("click");
            //$(document).off("click");
            parent.off("clickOff");
        }
        clicked = false;
    });
};

Использование:

$("#myDiv").clickOff(function() {
   alert('clickOff');
});
1

Чтобы скрыть fileTreeClass, если вы щелкнули вне него

 jQuery(document).mouseup(function (e) {
            var container = $(".fileTreeClass");
            if (!container.is(e.target) // if the target of the click isn't the container...
                && container.has(e.target).length === 0) // ... nor a descendant of the container
            {
                container.hide();
            }
        });
0

Я думаю, что этот парень получил лучшее решение Решение

0

Я пишу функцию для обнаружения щелчка элемента или нет:

function element_is_clicked (event, elementAccess) {
    /**
     * checks if clicked inside an element area or not
     * ***********************************************/
    if ($(elementAccess).is(':visible') == false) return false;

    var click_x = e.pageX;
    var click_y = e.pageY;
    var frame_left = $(elementAccess).offset().left; //console.log(frame_x);
    var frame_top = $(elementAccess).offset().top; //console.log(frame_x);
    var frame_width = $(elementAccess).width();
    var frame_height = $(elementAccess).height();

    if (click_x < frame_left) { return false; }
    if (click_y < frame_top) { return false; }
    if (click_x > frame_left + frame_width) { return false; }
    if (click_y > frame_top + frame_height) { return false; }

    return true;
};

использование:

$(document).click(function(event){
    if (element_is_clicked (event, '#my-element')) console.log('yes');
    else console.log('not');
});
0

если вы просто хотите отобразить окно, когда вы нажимаете на кнопку, и не просматривайте это окно, когда вы нажимаете на кнопку снаружи (или на кнопке снова), этот ниже работает хорошо

document.body.onclick = function() { undisp_menu(); };
var menu_on = 0;

function menu_trigger(event){

    if (menu_on == 0)
    {
        // otherwise u will call the undisp on body when 
        // click on the button
        event.stopPropagation(); 

        disp_menu();
    }

    else{
        undisp_menu();
    }

}


function disp_menu(){

    menu_on = 1;
    var e = document.getElementsByClassName("menu")[0];
    e.className = "menu on";

}

function undisp_menu(){

    menu_on = 0;
    var e = document.getElementsByClassName("menu")[0];
    e.className = "menu";

}

не забудьте это для кнопки

<div class="button" onclick="menu_trigger(event)">

<div class="menu">

и css:

.menu{
    display: none;
}

.on {
    display: inline-block;
}
0
$(document).on('click.menu.hide', function(e){
  if ( !$(e.target).closest('#my_menu').length ) {
    $('#my_menu').find('ul').toggleClass('active', false);
  }
});

$(document).on('click.menu.show', '#my_menu li', function(e){
  $(this).find('ul').toggleClass('active');
});
div {
  float: left;
}

ul {
  padding: 0;
  position: relative;
}
ul li {
  padding: 5px 25px 5px 10px;
  border: 1px solid silver;
  cursor: pointer;
  list-style: none;
  margin-top: -1px;
  white-space: nowrap;
}
ul li ul:before {
  margin-right: -20px;
  position: absolute;
  top: -17px;
  right: 0;
  content: "\25BC";
}
ul li ul li {
  visibility: hidden;
  height: 0;
  padding-top: 0;
  padding-bottom: 0;
  border-width: 0 0 1px 0;
}
ul li ul li:last-child {
  border: none;
}
ul li ul.active:before {
  content: "\25B2";
}
ul li ul.active li {
  display: list-item;
  visibility: visible;
  height: inherit;
  padding: 5px 25px 5px 10px;
}
<script src="/jquery-2.1.4.js"></script>
<div>
  <ul id="my_menu">
    <li>Menu 1
      <ul>
        <li>subMenu 1</li>
        <li>subMenu 2</li>
        <li>subMenu 3</li>
        <li>subMenu 4</li>
      </ul>
    </li>
    <li>Menu 2
      <ul>
        <li>subMenu 1</li>
        <li>subMenu 2</li>
        <li>subMenu 3</li>
        <li>subMenu 4</li>
      </ul>
    </li>
    <li>Menu 3</li>
    <li>Menu 4</li>
    <li>Menu 5</li>
    <li>Menu 6</li>
  </ul>
</div>

Вот версия jsbin http://jsbin.com/xopacadeni/edit?html,css,js,output

0

Подписать этап захвата щелчка, чтобы обработать щелчок по элементам, которые вызывают preventDefault.
Верните его на элемент документа, используя другое имя click-anywhere.

document.addEventListener('click', function (event) {
  event = $.event.fix(event);
  event.type = 'click-anywhere';
  $document.trigger(event);
}, true);

Затем, когда вам нужно щелкнуть внешнюю функцию, подпишитесь на click-anywhere событие на document и проверьте, не был ли щелчок за пределами интересующего вас элемента:

$(document).on('click-anywhere', function (event) {
  if (!$(event.target).closest('#smth').length) {
    // Do anything you need here
  }
});

Некоторые примечания:

  • Вы должны использовать document, так как это будет ошибка производительности для запуска события на всех элементах, за пределами которых произошел щелчок.

  • Эта функция может быть включена в специальный плагин, который вызывает обратный вызов при внешнем щелчке.

  • Вы не можете подписаться на фазу захвата, используя сам jQuery.

  • Вам не нужно загружать документы для подписки, поскольку подписка на document, даже не на ее body, поэтому она всегда существует независимо от состояния размещения и загрузки.

    script/li >
  • 0
    +1 за упоминание фазы capture - безусловно, снижает риск того, что stopPropagation() вещи (в зависимости от того, куда вы подключаете слушателя).
0

Попробуйте следующее:

$('html').click(function(e) {
  if($(e.target).parents('#menuscontainer').length == 0) {
    $('#menuscontainer').hide();
  }
});

https://jsfiddle.net/4cj4jxy0/

Но обратите внимание, что это не может работать, если событие click не может достигнуть тега html. (Возможно, другие элементы имеют stopPropagation()).

0

$('html').click(function() {
//Hide the menus if visible
});

$('#menucontainer').click(function(event){
    event.stopPropagation();
});
<script src="/jquery.min.js"></script>
<html>
 <button id='#menucontainer'>Ok</button> 
</html>
0
    $('#menucontainer').click(function(e){
        e.stopPropagation();
     });

    $(document).on('click',  function(e){
        // code
    });
0

Это приведет к переключению меню Nav, когда вы нажимаете на элемент /off.

$(document).on('click', function(e) {
    var elem = $(e.target).closest('#menu'),
    box = $(e.target).closest('#nav');
 if (elem.length) {
    e.preventDefault();
    $('#nav').toggle();
  } else if (!box.length) {
    $('#nav').hide();
 }
});



<li id="menu"><a></a></li>
<ul id="nav" >  //Nav will toggle when you Click on Menu(it can be an icon in this example)
        <li class="page"><a>Page1</a></li>
        <li class="page"><a>Pag2</a></li>
        <li class="page"><a>Page3</a></li>            
        <li class="page"><a>Page4</a></li>
</ul>
0

Это более общее решение, которое позволяет просматривать несколько элементов и динамически добавлять и удалять элементы из очереди.

Он содержит глобальную очередь (autoCloseQueue) - контейнер объектов для элементов, которые должны быть закрыты на внешних кликах.

Каждый ключ объекта очереди должен быть идентификатором DOM Element, а значение должно быть объектом с двумя функциями обратного вызова:

 {onPress: someCallbackFunction, onOutsidePress: anotherCallbackFunction}

Поместите это в ваш готовый обратный вызов документа:

window.autoCloseQueue = {}  

$(document).click(function(event) {
    for (id in autoCloseQueue){
        var element = autoCloseQueue[id];
        if ( ($(e.target).parents('#' + id).length) > 0) { // This is a click on the element (or its child element)
            console.log('This is a click on an element (or its child element) with  id: ' + id);
            if (typeof element.onPress == 'function') element.onPress(event, id);
        } else { //This is a click outside the element
            console.log('This is a click outside the element with id: ' + id);
            if (typeof element.onOutsidePress == 'function') element.onOutsidePress(event, id); //call the outside callback
            delete autoCloseQueue[id]; //remove the element from the queue
        }
    }
});

Затем, когда создается элемент DOM с id ' menuscontainer', просто добавьте этот объект в очередь:

window.autoCloseQueue['menuscontainer'] = {onOutsidePress: clickOutsideThisElement}
0

Просто предупреждение, что с помощью этого:

$('html').click(function() {
  // Hide the menus if visible
});

$('#menucontainer').click(function(event){
  event.stopPropagation();
});

Он предотвращает Ruby on Rails драйвер UJS от правильной работы. Например, link_to 'click', '/url', :method => :delete не будет работать.

Это может быть обходным путем:

$('html').click(function() {
  // Hide the menus if visible
});

$('#menucontainer').click(function(event){
  if (!$(event.target).data('method')) {
    event.stopPropagation();
  }
});
-1

Использование not():

$("#id").not().click(function() {
    alert('Clicked other that #id');
});
  • 3
    Это не работает not() удаляет элемент из списка выбранных элементов ( api.jquery.com/not ). Без селектора в качестве его параметра он ничего не делает, возвращая $('#id') что в точности противоположно тому, что мы пытаемся выполнить.
-2

Плагин внешнего доступа!

Применение:

$('.target-element').outsideClick(function(event){
    //code that fires when user clicks outside the element
    //event = the click event
    //$(this) = the '.target-element' that is firing this function 
}, '.excluded-element')

Код для этого:

(function($) {

//when the user hits the escape key, it will trigger all outsideClick functions
$(document).on("keyup", function (e) {
    if (e.which == 27) $('body').click(); //escape key
});

//The actual plugin
$.fn.outsideClick = function(callback, exclusions) {
    var subject = this;

    //test if exclusions have been set
    var hasExclusions = typeof exclusions !== 'undefined';

    //switches click event with touch event if on a touch device
    var ClickOrTouchEvent = "ontouchend" in document ? "touchend" : "click";

    $('body').on(ClickOrTouchEvent, function(event) {
        //click target does not contain subject as a parent
        var clickedOutside = !$(event.target).closest(subject).length;

        //click target was on one of the excluded elements
        var clickedExclusion = $(event.target).closest(exclusions).length;

        var testSuccessful;

        if (hasExclusions) {
            testSuccessful = clickedOutside && !clickedExclusion;
        } else {
            testSuccessful = clickedOutside;
        }

        if(testSuccessful) {
            callback.call(subject, event);
        }
    });

    return this;
};

}(jQuery));

Адаптировано из этого ответа https://stackoverflow.com/questions/152975/how-do-i-detect-a-click-outside-an-element

-2

Это классический случай, когда исправление к HTML будет лучшим решением. Почему бы не установить щелчок на элементах, которые не содержат элемент меню? Тогда вам не нужно добавлять распространение.

$('.header, .footer, .main-content').click(function() {
//Hide the menus if visible
});
  • 0
    Это плохая идея, потому что, если позже вы решите добавить какой-нибудь нижний колонтитул2 или что-то еще между ними, вам нужно будет не забыть добавить и его. $ ('html'). click () проще и позволяет работать в любом случае.
-2
$("body > div:not(#dvid)").click(function (e) {
    //your code
}); 
  • 0
    Добавить обработчик кликов для каждого другого элемента? Вы убьете производительность. Есть гораздо лучшие способы сделать это.
  • 0
    Это добавляет обработчик клика ко всем элементам div, кроме #dvid, это очень дорого для страницы, и если у вас есть какой-то div внутри #divid, у них есть обработчик, и это может быть неожиданным эффектом
-2

Вы можете установить tabindex в элемент DOM. Это вызовет событие размытия, когда пользователь выйдет за пределы элемента DOM.

Демо

<div tabindex="1">
    Focus me
</div>

document.querySelector("div").onblur = function(){
   console.log('clicked outside')
}
document.querySelector("div").onfocus = function(){
   console.log('clicked inside')
}
-2

Попробуйте этот код:

if ($(event.target).parents().index($('#searchFormEdit')) == -1 &&
    $(event.target).parents().index($('.DynarchCalendar-topCont')) == -1 &&
    (_x < os.left || _x > (os.left + 570) || _y < os.top || _y > (os.top + 155)) &&
    isShowEditForm) {

    setVisibleEditForm(false);
}
-3
$("html").click(function(){
    if($('#info').css("opacity")>0.9) {
        $('#info').fadeOut('fast');
    }
});
-3

Как обертка к этому замечательному ответу из Art и для использования синтаксиса, первоначально запрошенного OP, здесь расширение jQuery, которое может записывать клик, произошедший за пределами элемент набора.

$.fn.clickOutsideThisElement = function (callback) {
    return this.each(function () {
        var self = this;
        $(document).click(function (e) {
            if (!$(e.target).closest(self).length) {
                callback.call(self, e)
            }
        })
    });
};

Затем вы можете вызвать вот так:

$("#menuscontainer").clickOutsideThisElement(function() {
    // handle menu toggle
});

Здесь демо в скрипке

-3
 <div class="feedbackCont" onblur="hidefeedback();">
        <div class="feedbackb" onclick="showfeedback();" ></div>
        <div class="feedbackhide" tabindex="1"> </div>
 </div>

function hidefeedback(){
    $j(".feedbackhide").hide();
}

function showfeedback(){
    $j(".feedbackhide").show();
    $j(".feedbackCont").attr("tabindex",1).focus();
}

Это самое простое решение, с которым я столкнулся.

-3

Если вы создаете элементы динамически и используете функцию "live" для захвата событий, stopPropagation может не работать. В этом случае вы можете попробовать stopImmediatePropagation.

-4

Стандартный HTML:

Окружать меню с помощью <label> и получить изменения состояния фокусировки.

http://jsfiddle.net/bK3gL/

Плюс: вы можете развернуть меню Tab.

  • 0
    плюс: вы можете развернуть меню с помощью <kbd> tab </ kbd>

Ещё вопросы

Сообщество Overcoder
Наверх
Меню