How do I detect a click outside an element?

javascript jquery

1114192 просмотра

30 ответа

32432 Репутация автора

I have some HTML menus, which I show completely when a user clicks on the head of these menus. I would like to hide these elements when the user clicks outside the menus' area.

Is something like this possible with jQuery?

$("#menuscontainer").clickOutsideThisElement(function() {
    // Hide the menus
});
Автор: Sergio del Amo Источник Размещён: 30.09.2008 01:17

Ответы (30)


19 плюса

4544 Репутация автора

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

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

Автор: Chris MacDonald Размещён: 30.09.2008 01:20

1728 плюса

76773 Репутация автора

Решение

NOTE: Using stopEventPropagation() is something that should be avoided as it breaks normal event flow in the DOM. See this article for more information. Consider using this method instead

Attach a click event to the document body which closes the window. Attach a separate click event to the container which stops propagation to the document body.

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

$('#menucontainer').click(function(event){
    event.stopPropagation();
});
Автор: Eran Galperin Размещён: 30.09.2008 01:38

123 плюса

6921 Репутация автора

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

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

  event.stopPropagation();
});

Больше информации о функции jQueryone()

Автор: Joe Lencioni Размещён: 30.09.2008 06:13

136 плюса

2008 Репутация автора

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

if(!$(event.target).is('#foo'))
{
    // hide menu
}
Автор: Dennis Размещён: 06.07.2009 11:10

8 плюса

0 Репутация автора

Если вы пишете сценарии для 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;}
Автор: Erik Размещён: 14.08.2009 02:08

38 плюса

0 Репутация автора

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

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

Автор: user212621 Размещён: 17.11.2009 06:13

37 плюса

7554 Репутация автора

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

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

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

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

Автор: Wolfram Размещён: 05.04.2010 10:07

1279 плюса

12562 Репутация автора

You can listen for a click event on document and then make sure #menucontainer is not an ancestor or the target of the clicked element by using .closest().

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

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

Редактировать - 2017-06-23

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

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

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

  document.addEventListener('click', outsideClickListener)
}

Редактировать - 2018-03-11

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

function hideOnClickOutside(element) {
    const outsideClickListener = event => {
        if (!element.contains(event.target) && isVisible(element)) { // or use: event.target.closest(selector) === null
          element.style.display = 'none'
          removeClickListener()
        }
    }

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

    document.addEventListener('click', outsideClickListener)
}

const isVisible = elem => !!elem && !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ) // source (2018-03-11): https://github.com/jquery/jquery/blob/master/src/css/hiddenVisibleSelectors.js 

ПРИМЕЧАНИЕ: это основано на комментарии Алекса, чтобы просто использовать !element.contains(event.target)вместо части jQuery.

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

Автор: Art Размещён: 12.06.2010 08:35

17 плюса

772 Репутация автора

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

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

Автор: Chu Yeow Размещён: 02.12.2010 09:53

8 плюса

147 Репутация автора

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

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();
});
Автор: webenformasyon Размещён: 09.01.2011 11:47

25 плюса

3317 Репутация автора

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

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

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

13 плюса

834 Репутация автора

Я нашел этот метод в каком-то плагине календаря 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);
Автор: nazar kuliyev Размещён: 28.07.2011 02:06

5 плюса

71 Репутация автора

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

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

Автор: Rowan Размещён: 20.09.2011 06:44

5 плюса

1412 Репутация автора

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

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

Автор: Satya Prakash Размещён: 06.12.2011 01:42

24 плюса

510 Репутация автора

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

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

    })
});
Автор: benb Размещён: 22.12.2011 11:59

6 плюса

191768 Репутация автора

Подцепите слушателя события щелчка на документе. Внутри прослушивателя событий вы можете посмотреть на объект события , в частности, на 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");
    }
});
Автор: Salman A Размещён: 03.05.2012 09:21

31 плюса

156 Репутация автора

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

$('html').click(function (e) {
    if (e.target.id == 'YOUR-DIV-ID') {
        //do something
    } else {
        //do something
    }
});
Автор: srinath Размещён: 04.06.2012 02:08

8 плюса

893 Репутация автора

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

$(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");
Автор: mems Размещён: 05.06.2013 03:05

16 плюса

1279 Репутация автора

Как вариант:

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.

Автор: Bohdan Lyzanets Размещён: 24.07.2014 09:41

21 плюса

4450 Репутация автора

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
    }
});
Автор: Iman Sedighi Размещён: 28.01.2015 05:24

12 плюса

13923 Репутация автора

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

Автор: Tiny Giant Размещён: 19.05.2015 10:52

5 плюса

219 Репутация автора

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

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

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

Автор: bbe Размещён: 25.07.2015 09:33

36 плюса

2564 Репутация автора

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

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

<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>
Автор: Rameez Rami Размещён: 02.11.2015 08:33

23 плюса

530 Репутация автора

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

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

Автор: Jitendra Damor Размещён: 15.12.2015 03:50

5 плюса

660 Репутация автора

Для более легкого использования и более выразительного кода я создал для этого плагин 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...
});
Автор: Matt Goodwin Размещён: 21.04.2016 08:04

255 плюса

136469 Репутация автора

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

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

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

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

Подсказка: это слово «клик» !

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

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

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

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

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

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

focusout событие

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

Подсказка: будьте осторожны с blurсобытием, blurне распространяется, если событие было связано с фазой барботирования!

JQuery's 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 , однако я настоятельно рекомендую разработчикам обратиться к спецификации для получения подробной информации. на какие роли они должны использовать и любые другие соответствующие атрибуты.

Автор: zzzzBov Размещён: 11.07.2016 11:29

13 плюса

3263 Репутация автора

Событие имеет свойство с именем 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
  }
});
Автор: Dan Philip Размещён: 14.04.2017 04:27

5 плюса

72 Репутация автора

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

});

Автор: Walt Размещён: 18.07.2017 12:43

5 плюса

20417 Репутация автора

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

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

Автор: Rinto George Размещён: 14.01.2018 08:29

10 плюса

137 Репутация автора

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

Автор: Jovanni G Размещён: 12.02.2018 10:39
Вопросы из категории :
32x32