Щелкните за пределами меню, чтобы закрыть в jquery

107

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

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

Это упрощенная версия того, что у меня есть сейчас:

$(document).ready(function() {
  $("ul.opMenu li").click(function(){
   $('#MainOptSubMenu',this).css('visibility', 'visible');
  });

  $("ul.opMenu li").mouseleave(function(){
      $('#MainOptSubMenu',this).css('visibility', 'hidden');
  });
});



<ul  class="opMenu">
  <li id="footwo" class="">
    <span id="optImg" style="display: inline-block;"> <img src="http://localhost.vmsinfo.com:8002/insight/images/options-hover2.gif"/> </span>
      <ul id="MainOptSubMenu" style="visibility: hidden; top: 25px; border-top: 0px solid rgb(217, 228, 250); background-color: rgb(217, 228, 250); padding-bottom: 15px;">
        <li>some</li>
       <li>nav</li>
       <li>links</li>
       </ul>
    </li>
</ul> 

Я пробовал что-то вроде этого, $('document[id!=MainOptSubMenu]').click(function()думая, что это сработает для всего, что не было в меню, но это не сработало.

Джордж
источник

Ответы:

196

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

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

Прикрепите событие щелчка к тексту документа, которое закрывает окно. Прикрепите к окну отдельное событие щелчка, которое останавливает распространение в тексте документа.
$('html').click(function() {
  //Hide the menus if visible
});

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

Джон В
источник
16
это очень красиво, но вы должны использовать $ ('html'). click (), а не тело. Тело всегда имеет высоту своего содержимого. Если контента мало или экран очень высокий, работает только на части, заполненной телом. Копия из: stackoverflow.com/questions/152975/… - meo, 25 фев. 2011, в 15:35
NickGreen
отличное решение. есть идеи, почему он работает с .click (), а не с .live ('click', func ...?
Hat
3
Единственная проблема с этим подходом заключается в том, что объекты, у которых уже есть прослушиватель щелчков, который stopPropagation по другим причинам не действует. Я понимаю, почему это так, но это все еще ограничение этого решения.
DanH
53

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

$('a#menu-link').on('click', function(e) {
    e.preventDefault();
    e.stopPropagation();

    $('#menu').toggleClass('open');

    $(document).one('click', function closeMenu (e){
        if($('#menu').has(e.target).length === 0){
            $('#menu').removeClass('open');
        } else {
            $(document).one('click', closeMenu);
        }
    });
});

Изменить: если вы хотите избежать stopPropagation()начальной кнопки, вы можете использовать это

var $menu = $('#menu');

$('a#menu-link').on('click', function(e) {
    e.preventDefault();

    if (!$menu.hasClass('active')) {
        $menu.addClass('active');

        $(document).one('click', function closeTooltip(e) {
            if ($menu.has(e.target).length === 0 && $('a#menu-link').has(e.target).length === 0) {
                $menu.removeClass('active');
            } else if ($menu.hasClass('active')) {
                $(document).one('click', closeTooltip);
            }
        });
    } else {
        $menu.removeClass('active');
    }
});
адриенденат
источник
Я часто вижу такой код, разве это не добавит новую функцию щелчка в документ каждый раз, когда щелкают ссылку меню? Итак, если вы щелкните ссылку меню 1000 раз, не обновляя страницу, у вас будет 1000 функций, которые занимают память и также выполняются?
eselk 08
Да
ладно
2
хорошее решение, но мне пришлось использовать e.stopPropagation()в Chrome, чтобы остановить срабатывание первого события в one()обработчике
antfx
18

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

Вот простое решение, основанное на ответе пользователя 2989143 :

$('html').click(function(event) {
    if ($(event.target).closest('#menu-container, #menu-activator').length === 0) {
        $('#menu-container').hide();
    }
});
Code Commander
источник
17

Если в вашем случае использование плагина подходит, то я предлагаю плагин Clickoutside Бена Алмана, расположенный здесь :

его использование так же просто:

$('#menu').bind('clickoutside', function (event) {
    $(this).hide();
});

надеюсь это поможет.

Подсознательный хеш
источник
8

2 варианта, которые вы можете изучить:

  • При отображении меню поместите за ним большой пустой DIV, закрывающий остальную часть страницы, и дайте ему событие щелчка, чтобы закрыть меню (и само). Это похоже на методы, используемые с лайтбоксами, когда щелчок по фону закрывает лайтбокс.
  • При отображении меню прикрепите обработчик события одноразового щелчка к телу, закрывающему меню. Для этого вы используете jQuery .one ().
DA.
источник
6

Я нашел вариант решения Grsmto в и решение Денниса сосредоточил свой вопрос.

$(".MainNavContainer").click(function (event) {
    //event.preventDefault();  // Might cause problems depending on implementation
    event.stopPropagation();

    $(document).one('click', function (e) {
        if(!$(e.target).is('.MainNavContainer')) {
            // code to hide menus
        }
    });
});
cat5dev
источник
5

Я использую это решение с несколькими элементами с одинаковым поведением на одной странице:

$("html").click(function(event){
    var otarget = $(event.target);
    if (!otarget.parents('#id_of element').length && otarget.attr('id')!="id_of element" && !otarget.parents('#id_of_activator').length) {
        $('#id_of element').hide();
    }
});

stopPropagation () - плохая идея, это нарушает стандартное поведение многих вещей, включая кнопки и ссылки.

user2989143
источник
Это здорово, но вы можете упростить это как$target.closest('#menu-container, #menu-activator').length === 0
Code Commander
5

Недавно я столкнулся с той же проблемой. Я написал следующий код:

    $('html').click(function(e) {
      var a = e.target;
      if ($(a).parents('.menu_container').length === 0) {
        $('.ofSubLevelLinks').removeClass('active'); //hide menu item
        $('.menu_container li > img').hide(); //hide dropdown image, if any
     }
    });

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

Namish
источник
4

как насчет этого?

    $(this).mouseleave(function(){  
        var thisUI = $(this);
        $('html').click(function(){
                thisUI.hide();
            $('html').unbind('click');
         });
     });
cgfix
источник
3

Я считаю более полезным использовать mousedown-event вместо click-event. Событие клика не работает, если пользователь нажимает на другие элементы на странице с событиями клика. В сочетании с методом one () jQuery это выглядит так:

$("ul.opMenu li").click(function(event){

   //event.stopPropagation(); not required any more
   $('#MainOptSubMenu').show();

   // add one mousedown event to html
   $('html').one('mousedown', function(){
       $('#MainOptSubMenu').hide();
   });
});

// mousedown must not be triggered inside menu
$("ul.opMenu li").bind('mousedown', function(evt){
    evt.stopPropagation();
});
йэно
источник
3

даже я столкнулся с такой же ситуацией, и один из моих наставников поделился этой идеей со мной.

step: 1 при нажатии на кнопку, на которой мы должны показать раскрывающееся меню. затем добавьте указанное ниже имя класса "more_wrap_background" на текущую активную страницу, как показано ниже.

$('.ui-page-active').append("<div class='more_wrap_background' id='more-wrap-bg'> </div>");

step-2 затем добавьте щелчки для тега div, например

$(document).on('click', '#more-wrap-bg', hideDropDown);

где hideDropDown - это функция, вызываемая для скрытия раскрывающегося меню

Шаг 3 и важный шаг при скрытии раскрывающегося меню - это удаление того класса, который вы добавили ранее, например

$('#more-wrap-bg').remove();

Я удаляю, используя его идентификатор в приведенном выше коде

.more_wrap_background {
  top: 0;
  padding: 0;
  margin: 0;
  background: rgba(0, 0, 0, 0.1);
  position: fixed;
  display: block;
  width: 100% !important;
  z-index: 999;//should be one less than the drop down menu's z-index
  height: 100% !important;
}
Абхиман
источник
2
$("html").click( onOutsideClick );
onOutsideClick = function( e )
{
    var t = $( e.target );
    if ( !(
        t.is("#mymenu" ) ||     //Where #mymenu - is a div container of your menu
        t.parents( "#mymenu" ).length > 0
        )   )
    {
        //TODO: hide your menu
    }
};

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

Олег Всильдеревьев
источник
1

Думаю, вам нужно что-то вроде этого: http://jsfiddle.net/BeenYoung/BXaqW/3/

$(document).ready(function() {
  $("ul.opMenu li").each(function(){
      $(this).click(function(){
            if($(this).hasClass('opened')==false){          
                $('.opMenu').find('.opened').removeClass('opened').find('ul').slideUp();
                $(this).addClass('opened'); 
                $(this).find("ul").slideDown();
            }else{
                $(this).removeClass('opened'); 
                $(this).find("ul").slideUp();               
            }
      });
  });    
});

Надеюсь, это пригодится вам!

Саката Гинтоки
источник
1

Используйте селектор ': visible'. Где .menuitem - это элементы, которые нужно скрыть ...

$('body').click(function(){
  $('.menuitem:visible').hide('fast');
});

Или, если у вас уже есть элементы .menuitem в var ...

var menitems = $('.menuitem');
$('body').click(function(){
  menuitems.filter(':visible').hide('fast');
});
Экернер
источник
0

Это не должно быть сложно.

$(document).on('click', function() {
    $("#menu:not(:hover)").hide();
});
Джонатан Гауде
источник