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

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

возможно ли что-то подобное с jQuery?

$("#menuscontainer").clickOutsideThisElement(function() {
    // Hide the menus
});

30 ответов


Примечание: использование stopEventPropagation() - это то, чего следует избегать, поскольку это нарушает нормальный поток событий в DOM. См.в этой статье для получения дополнительной информации. Рассмотрите возможность использования этот метод вместо.

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

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

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

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

если это не так, то щелкнутый элемент находится вне #menucontainer и можно спокойно скрыть.

$(document).click(function(event) { 
    if(!$(event.target).closest('#menucontainer').length) {
        if($('#menucontainer').is(":visible")) {
            $('#menucontainer').hide();
        }
    }        
});

изменить – 2017-06-23

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

export function hideOnClickOutside(selector) {
  const outsideClickListener = (event) => {
    if (!$(event.target).closest(selector).length) {
      if ($(selector).is(':visible')) {
        $(selector).hide()
        removeClickListener()
      }
    }
  }

  const removeClickListener = () => {
    document.removeEventListener('click', outsideClickListener)
  }

  document.addEventListener('click', outsideClickListener)
}

Edit-2018-03-11

для тех, кто не хочет использовать jQuery. Вот приведенный выше код в простом vanillaJS (ECMAScript6).

function hideOnClickOutside(element) {
    const outsideClickListener = event => {
        if (!element.contains(event.target)) { // or use: event.target.closest(selector) === null
            if (isVisible(element)) {
                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 

Примечание: Это основано на комментарии Alex, чтобы просто использовать !element.contains(event.target) вместо части jQuery.

но element.closest() теперь также доступен во всех основных браузерах (версия W3C немного отличается от версии jQuery). Polyfills можно найти здесь:https://developer.mozilla.org/en-US/docs/Web/API/Element/closest


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

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

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

Подсказка: это слово "выберите"!

вы на самом деле не хотят связывать обработчики click.

если вы привязываете обработчики щелчков, чтобы закрыть диалоговое окно, вы уже потерпели неудачу. Причина, по которой вы потерпели неудачу заключается в том, что не все триггеры click событий. Пользователи, не использующие мышь сможет избежать вашего диалога (и Ваше всплывающее меню, возможно, является типом диалога), нажав Tab, и тогда они не смогут прочитать содержимое за диалогом без последующего запуска 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>

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

первая ссылка в диалоге не кликабельна. Попытка щелкнуть по нему или вкладке к нему приведет к закрытию диалога до того, как произойдет взаимодействие. Это потому, что фокусировка внутреннего элемента вызывает 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="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>

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

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

это должно выглядеть знакомым
$('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="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>

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="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>

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

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="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/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, но и очень рекомендуется, чтобы разработчики ссылались на спецификацию для получения подробной информации о том, какие роли они должны использовать и любые другие соответствующие атрибуты.


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

if(!$(event.target).is('#foo'))
{
    // hide menu
}

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

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

  event.stopPropagation();
});

подробнее о jQuery one() функции


$("#menuscontainer").click(function() {
    $(this).focus();
});
$("#menuscontainer").blur(function(){
    $(this).hide();
});

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


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

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

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

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


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

$('html').click(function (e) {
    if (e.target.id == 'YOUR-DIV-ID') {
        //do something
    } else {
        //do something
    }
});

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

первый вариант

<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>

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

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

  1. если вы присоедините событие click обработчик элемента body во время щелчка обязательно дождитесь 2-го щелчка, прежде чем закрыть меню и отменить привязку события. В противном случае событие click, открывшее меню, всплывет до прослушивателя, который должен закрыть меню.
  2. если вы используете событие.stopPropogation () в событии click никакие другие элементы на Вашей странице не могут иметь функцию click-anywhere-to-close.
  3. бесконечное присоединение обработчика событий click к элементу body не является исполнителем решение
  4. сравнение цели события и его родителей с создателем обработчика предполагает, что вы хотите закрыть меню, когда вы нажимаете на него, когда вы действительно хотите закрыть его, когда вы нажимаете в любом месте страницы.
  5. прослушивание событий на элементе body сделает ваш код более хрупким. Стиль, как невинный, как это сломало бы его:body { margin-left:auto; margin-right: auto; width:960px;}

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

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

    })
});

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

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


простое решение для ситуации:

$(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 событие click срабатывает.

вы можете увидеть следующий блог для получения дополнительной информации:http://www.codecanal.com/detect-click-outside-div-using-javascript/


Решение 1

вместо использования события.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
    }
});

Как вариант:

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.


у меня был успех с чем-то вроде этого:

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


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

Если вы создаете сценарии для 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;}

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

$(document).on("click.menu-outside", function(event){
    // Test if target and it's 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)
    }
});

чтобы удалить click outside event listener, просто:

$(document).off("click.menu-outside");

вот решение vanilla JavaScript для будущих зрителей.

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

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

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

  1. добавить класс имя hidden складной элемент.
  2. после щелчка по документу закройте все скрытые элементы, которые не содержат нажатого элемента и не скрыты
  3. если нажатый элемент является переключателем, переключите указанный элемент.

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

событие имеет свойство event.путь элемента, который является "статический упорядоченный список всех своих предков, дерево". Чтобы проверить, возникло ли событие из определенного элемента DOM или одного из его дочерних элементов, просто проверьте путь для этого конкретного элемента DOM. Он также может использоваться для проверки нескольких элементов логически ORing элемент проверить в


подключить обработчик события click на документ. Внутри прослушивателя событий вы можете посмотреть на событие объекта в частности,событие.цель чтобы увидеть, какой элемент был выбран:

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

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

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

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

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


Upvote для наиболее популярного ответа, но добавить

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

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


для облегчения использования и более выразительного кода я создал плагин jQuery для этого:

$('div.my-element').clickOut(function(target) { 
    //do something here... 
});

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

плагин:

$.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;
};

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

используйте так:

$('div.my-element').clickOut($('div.my-parent'), function(target) { 
    //do something here...
});

Это мое решение этой проблемы:

$(document).ready(function() {
  $('#user-toggle').click(function(e) {
    $('#user-nav').toggle();
    e.stopPropagation();
  });

  $('body').click(function() {
    $('#user-nav').hide(); 
  });

  $('#user-nav').click(function(e){
    e.stopPropagation();
  });
});

Я закончил делать что-то вроде этого:

$(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, чтобы не пройти. Конечно, было бы неплохо иметь href, чтобы отвезти вас куда-нибудь, или вы могли бы назвать некоторые вещи ajax. В любом случае, для меня это нормально. Как раз то, что я хотел.


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

// 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');
    }
});

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


:

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