Щелкните за пределами меню, чтобы закрыть в 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();
    }
});

17

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

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

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

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


8

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

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

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

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 () - плохая идея, это нарушает стандартное поведение многих вещей, включая кнопки и ссылки.


Это здорово, но вы можете упростить это как$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
     }
    });

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


4

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

    $(this).mouseleave(function(){  
        var thisUI = $(this);
        $('html').click(function(){
                thisUI.hide();
            $('html').unbind('click');
         });
     });

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.