JS скрыть div по классу, если отображается, щелкнув всюду на странице (вне этого div) [dублировать]

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

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

$("#menuscontainer").clickOutsideThisElement(function() {
    // Hide the menus
});
 
2032
задан 30 October 2016 в 20:05

29 ответов

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

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

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

$('div.my-element').clickOut($('div.my-parent'), function(target) { 
    //do something here...
});
31
ответ дан 15 August 2018 в 14:30

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

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.

26
ответ дан 15 August 2018 в 14:30

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

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

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

12
ответ дан 15 August 2018 в 14:30

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

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

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

Edit - 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. Вот этот код в plain 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 

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

Но element.closest() теперь также доступен во всех основных браузерах (версия W3C немного отличается от jQuery). Полиполки можно найти здесь: .closest()

1132
ответ дан 15 August 2018 в 14:30
  • 1
    Я пробовал многие другие ответы, но только этот работал. Благодарю. Код, который я использовал, был следующим: $ (document) .click (function (event) {if ($ (event.target) .closest ('. Window'). Length == 0) {$ ('. Window' ) .fadeOut ('fast');}}); – Pistos 6 April 2012 в 00:30
  • 2
    Я на самом деле закончил работу с этим решением, потому что он лучше поддерживает несколько меню на той же странице, где нажатие на второе меню, в то время как первое открыто, оставит первое открытое решение stopPropagation. – umassthrower 8 April 2012 в 04:41
  • 3
    Это очень хорошее решение для нескольких элементов на странице. – jsgroove 4 December 2012 в 21:19
  • 4
    Отличный ответ. Это путь, когда у вас есть несколько предметов, которые вы хотите закрыть. – John 8 May 2013 в 20:23
  • 5
    Без jQuery - !element.contains(event.target) с помощью Node.contains () – Alex Ross 31 July 2015 в 04:56

Событие имеет свойство, называемое event.path элемента, который является «статическим упорядоченным списком всех его предков в древовидном порядке». Чтобы проверить, произошло ли событие из определенного элемента DOM или одного из его дочерних элементов, просто проверьте путь к этому конкретному элементу DOM. Он также может быть использован для проверки нескольких элементов логически OR при проверке элемента в функции some.

$("body").click(function() {
  target = document.getElementById("main");
  flag = event.path.some(function(el, i, arr) {
    return (el == target)
  })
  if (flag) {
    console.log("Inside")
  } else {
    console.log("Outside")
  }
});
#main {
  display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="main">
  <ul>
    <li>Test-Main</li>
    <li>Test-Main</li>
    <li>Test-Main</li>
    <li>Test-Main</li>
    <li>Test-Main</li>
  </ul>
</div>
<div id="main2">
  Outside Main
</div>

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

$("body").click(function() {
  target = $("#menuscontainer")[0];
  flag = event.path.some(function(el, i, arr) {
    return (el == target)
  });
  if (!flag) {
    // Hide the menus
  }
});
7
ответ дан 15 August 2018 в 14:30

Решение1

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

Solution1

С помощью всего лишь простого условия 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
    }
});
15
ответ дан 15 August 2018 в 14:30
  • 1
    Я использовал это решение с булевым флагом, и это хорошо также с сочлененной DOm, а также если внутри #menucontainer есть много других элементов – Migio B 1 March 2015 в 00:28

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

$(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, если вне события [click] [3] вызывается

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

16
ответ дан 15 August 2018 в 14:30

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

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

<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>
25
ответ дан 15 August 2018 в 14:30
  • 1
    Третье решение на сегодняшний день является самым элегантным способом проверки. Это также не связано с чрезмерной нагрузкой на jQuery. Очень хорошо. Это очень помогло. Благодарю. – dbarth 27 July 2016 в 17:49
  • 2
    Я пытаюсь получить третье решение с несколькими элементами document.getElementsByClassName, если у кого-то есть ключ, пожалуйста, поделитесь. – lowtechsun 16 January 2017 в 12:42
  • 3
    @lowtechsun Вам нужно пройти, чтобы проверить каждый. – Donnie D'Amato 13 February 2017 в 21:28
  • 4
    Действительно понравилось третье решение, однако триггеры щелчка перед моим div начали показывать, что скрывает его снова, любая идея, почему? – indiehjaerta 14 April 2018 в 03:49

Функция:

$(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();
}
4
ответ дан 15 August 2018 в 14:30
  • 1
    Не будет ли это инициировать событие clickout также в том случае, если щелкнут дочерний элемент контейнера? – Jānis Elmeris 20 August 2012 в 19:28

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

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

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

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

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
ответ дан 15 August 2018 в 14:30

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

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

  event.stopPropagation();
});

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

118
ответ дан 15 August 2018 в 14:30
  • 1
    но тогда, если вы нажмете на самое меню, то снаружи, это не сработает :) – vsync 17 August 2009 в 21:46
  • 2
    Проблема заключается в том, что "один" применяется к методу jQuery для добавления событий в массив несколько раз. Поэтому, если вы нажмете на меню, чтобы открыть его более одного раза, событие снова привязывается к телу и пытается скрыть это меню несколько раз. Для устранения этой проблемы следует применять отказоустойчивость. – marksyzm 24 June 2013 в 16:01
  • 3
    После привязки с помощью .one - внутри обработчика $('html') - напишите $('html').off('click'). – Cody 13 July 2015 в 21:01
  • 4
    @Cody я не думаю, что это помогает. Обработчик one автоматически вызовет off (как показано в документах jQuery). – Mariano Desanze 4 December 2015 в 22:12
Как обнаружить клик вне элемента?

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

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

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

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

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

Подсказка: это слово «щелкнуть»!

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

Как закрыть диалоговое окно, когда пользователь закончил с ним?

Итак, давайте перефразируем вопрос.

Это цель. К сожалению, теперь нам нужно связать событие userisfinishedwiththedialog, и это связывание не так просто.

focusout event

Итак, как мы можем обнаружить, что пользователь закончил используя диалог?

Хорошим началом является определение того, оставил ли фокус диалог.

Подсказка: это слово «щелчок»!

element.addEventListener('blur', ..., true);
//                       use capture: ^^^^

jQuery's focusout будет отлично работать. Если вы не можете использовать jQuery, вы можете использовать blur на этапе захвата:

Кроме того, для многих диалогов вам нужно будет позволить контейнеру получить фокус. Добавьте 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.

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

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

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

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

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

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

Кнопка Esc

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

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

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

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

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

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

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

Роли WAI-ARIA и другая поддержка специальных возможностей

$('.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.

191
ответ дан 15 August 2018 в 14:30
  • 1
    Это самый полный ответ с учетом соображений и доступности. Я думаю, что это должен быть принятый ответ, так как большинство других ответов обрабатывают только щелчок и просто отбрасываются фрагментами кода без каких-либо объяснений. – Cyrille 29 October 2016 в 17:39
  • 2
    Удивительно, хорошо объяснено. Я просто использовал этот подход для компонента React и отлично работал – David Lavieri 18 November 2016 в 07:26
  • 3
    @zzzzBov благодарит за глубокий ответ, я пытаюсь добиться его в vanilla JS, и я немного потерял все вещи jquery. есть ли что-нибудь подобное в ваниле js? – HendrikEng 4 January 2017 в 07:33
  • 4
    @zzzzBov нет, я не ищу, чтобы вы, конечно, писали бесплатную версию jQuery, я постараюсь сделать это, и я думаю, что лучше всего задать здесь новый вопрос, если я действительно застрял. Еще раз спасибо. – HendrikEng 4 January 2017 в 16:19
  • 5
    Хотя это отличный метод для обнаружения щелчка по пользовательским раскрывающимся спискам или другим входам, он никогда не должен быть предпочтительным методом для модалов или всплывающих окон по двум причинам. Модальная функция закрывается, когда пользователь переключается на другую вкладку или окно или открывает контекстное меню, которое действительно раздражает. Кроме того, событие «клик» срабатывает при наведении курсора мыши, в то время как событие «focusout» запускает момент нажатия мыши. Обычно кнопка только выполняет действие, если вы нажимаете кнопку мыши вниз, а затем отпускаете. Правильный, доступный способ сделать это для модалов - это добавить кнопку закрытия tabbable. – Kevin 6 August 2017 в 16:11

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

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

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

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

8
ответ дан 15 August 2018 в 14:30

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

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

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

5
ответ дан 15 August 2018 в 14:30

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

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

    })
});
21
ответ дан 15 August 2018 в 14:30

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

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

17
ответ дан 15 August 2018 в 14:30

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

if(!$(event.target).is('#foo'))
{
    // hide menu
}
127
ответ дан 15 August 2018 в 14:30
  • 1
    Я опубликовал еще один практический пример того, как использовать event.target, чтобы избежать запуска другого виджета Jquery UI за пределами html-обработчиков при встраивании их в свой собственный pop-over box: Лучший способ получить исходную цель ] – Joey T 2 July 2012 в 23:13
  • 2
    Это сработало для меня, кроме того, что я добавил && !$(event.target).parents("#foo").is("#foo") внутри оператора IF, чтобы любые дочерние элементы не закрывали меню при нажатии. – honyovk 27 September 2012 в 00:57
  • 3
    @Frug Ваш ответ может быть ценным, поэтому вы получили 5, но я смутился вашим ответом. и кто MBJ. Я искал, чтобы создать лучшее решение, основанное на вашем ответе. – Satya Prakash 30 September 2013 в 19:25
  • 4
    MBJ, вероятно, было старым именем человека, который опубликовал комментарий чуть выше моего. Пользователи меняют имена, это может усложнить, но обратите внимание на комментарий выше моего имеет добавленный .parents("#foo") – Frug 5 November 2013 в 06:52
  • 5
    Кратким улучшением обработки глубокой вложенности является использование .is('#foo, #foo *'), но я не рекомендую привязывать обработчики кликов для решения этой проблемы . – zzzzBov 19 September 2016 в 19:03

Если вы работаете с сценарием для 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;}
8
ответ дан 15 August 2018 в 14:30

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

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

Чтобы удалить клик на внешнем прослушивателе событий, просто:

$(document).off("click.menu-outside");
8
ответ дан 15 August 2018 в 14:30
  • 1
    Для меня это было лучшим решением. Использовал его в обратном вызове после анимации, поэтому мне понадобилось какое-то событие отсоединения. +1 – qwertzman 29 May 2014 в 16:09
  • 2
    Здесь есть лишняя проверка. если элемент действительно #menuscontainer, вы все еще проходите через его родителей. вы должны сначала проверить это, и если это не тот элемент, затем перейдите в дерево DOM. – vsync 25 August 2014 в 19:25
  • 3
    Правильно ! Вы можете изменить условие на if(!($(event.target).is("#menuscontainer") || $(event.target).parents().is("#menuscontainer"))){. Это небольшая оптимизация, но она встречается всего несколько раз в вашей жизненной программе: для каждого клика, если зарегистрировано событие click.menu-outside. Это длиннее (+32 символа) и не использует цепочку методов – mems 25 August 2014 в 19:40
$(document).click(function() {
    $(".overlay-window").hide();
});
$(".overlay-window").click(function() {
    return false;
});

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

5
ответ дан 15 August 2018 в 14:30

Захватите прослушиватель событий щелчка на документе. Внутри прослушивателя событий вы можете посмотреть объект события, в частности, 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");
    }
});
6
ответ дан 15 August 2018 в 14:30

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

// 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
ответ дан 15 August 2018 в 14:30

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

$(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();
  });
});
4
ответ дан 15 August 2018 в 14:30
$("#menuscontainer").click(function() {
    $(this).focus();
});
$("#menuscontainer").blur(function(){
    $(this).hide();
});

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

32
ответ дан 15 August 2018 в 14:30
  • 1
    Это я использую. Это может быть не идеально, но как программист-хобби, его достаточно простой, чтобы понять ясно. – kevtrout 8 February 2010 в 20:05
  • 2
    blur - это перемещение за пределы #menucontainer, вопрос был о щелчке – borrel 6 August 2013 в 18:22
  • 3
    @borrel blur - не - перемещение за пределами контейнера. Blur - это противоположность фокуса, вы думаете о мыши. Это решение работало особенно хорошо для меня, когда я создавал & quot; click для редактирования & quot; текст, где я менял местами между текстовым и входным полями по щелчкам. – parker.sikand 11 October 2013 в 03:08
  • 4
    Мне пришлось добавить tabindex="-1" к #menuscontainer, чтобы он работал. Кажется, что если вы помещаете тег ввода внутри контейнера и нажимаете на него, контейнер скрывается. – tyrion 12 August 2014 в 18:17
  • 5
    событие mouseleave больше подходит для меню и контейнеров (ref: w3schools.com/jquery/… ) – Evgenia Manolova 19 October 2015 в 04:35

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

$(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
ответ дан 15 August 2018 в 14:30

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

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

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

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

31
ответ дан 15 August 2018 в 14:30

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

$('html').click(function (e) {
    if (e.target.id == 'YOUR-DIV-ID') {
        //do something
    } else {
        //do something
    }
});
26
ответ дан 15 August 2018 в 14:30

Другие вопросы по тегам:

Похожие вопросы: