Clique fora do menu para fechar no jquery

107

Portanto, tenho um menu suspenso que é exibido com um clique, de acordo com os requisitos de negócios. O menu fica oculto novamente depois que você move o mouse para longe dele.

Mas agora estou sendo solicitado a mantê-lo no lugar até que o usuário clique em qualquer lugar do documento. Como pode ser isto alcançado?

Esta é uma versão simplificada do que tenho agora:

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

Eu tentei algo assim $('document[id!=MainOptSubMenu]').click(function()pensando que acionaria qualquer coisa que não fosse o menu, mas não funcionou.

George
fonte

Respostas:

196

Dê uma olhada na abordagem que esta pergunta usou:

Como faço para detectar um clique fora de um elemento?

Anexe um evento de clique ao corpo do documento que fecha a janela. Anexe um evento de clique separado à janela que interrompe a propagação para o corpo do documento.
$('html').click(function() {
  //Hide the menus if visible
});

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

Jon W
fonte
16
é muito bonito, mas você deve usar $ ('html'). click () não no corpo. O corpo sempre tem a altura de seu conteúdo. Se não há muito conteúdo ou a tela é muito alta, só funciona na parte preenchida pelo corpo. Cópia de: stackoverflow.com/questions/152975/… - meo 25 de fevereiro '11 às 15:35
NickGreen
ótima solução. alguma ideia de por que funciona com .click () e não com .live ('click', func ...?
Hat
3
O único problema com essa abordagem é que os objetos que já têm um ouvinte de clique que stopPropagation por outros motivos não têm efeito. Eu entendo porque é assim, mas ainda é uma limitação desta solução.
DanH
53

A resposta está certa, mas adicionará um ouvinte que será acionado toda vez que ocorrer um clique em sua página. Para evitar isso, você pode adicionar o ouvinte apenas uma vez:

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

Editar: se você quiser evitar o stopPropagation()no botão inicial, você pode usar isso

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');
    }
});
adriendenat
fonte
Eu vejo muito código como este, isso não adicionaria uma nova função de clique ao documento toda vez que o link do menu for clicado? Então, se você clicar no link do menu 1000 vezes sem atualizar a página, você terá 1000 funções ocupando memória e também executando?
eselk
Esqueça, não vi a função "um", agora entendo por que funciona: api.jquery.com/one
eselk
2
boa solução, mas eu tive que usar e.stopPropagation()no Chrome para impedir o primeiro evento de disparar no one()manipulador
antfx
18

As stopPropagationopções são ruins porque podem interferir em outros manipuladores de eventos, incluindo outros menus que podem ter anexado manipuladores próximos ao elemento HTML.

Aqui está uma solução simples com base na resposta do usuário 2989143 :

$('html').click(function(event) {
    if ($(event.target).closest('#menu-container, #menu-activator').length === 0) {
        $('#menu-container').hide();
    }
});
Code Commander
fonte
17

Se usar um plug-in no seu caso, sugiro o plug-in clickoutside de Ben Alman localizado aqui :

seu uso é tão simples quanto este:

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

espero que isto ajude.

Hash Subliminal
fonte
8

2 opções que você pode investigar:

  • Ao mostrar o menu, coloque um grande DIV vazio atrás dele cobrindo o resto da página e dê um clique para fechar o menu (e ele mesmo). Istoéparecido com os métodos usados ​​com a caixa de luz onde clicar no fundo fecha a caixa de luz
  • Ao exibir o menu, anexe um manipulador de eventos de clique único no corpo que fecha o menu. Você usa '.one ()' do jQuery para isso.
DA.
fonte
6

Eu encontrei uma variante de solução de Grsmto e solução Dennis' fixa o meu problema.

$(".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
fonte
5

Eu uso esta solução com vários elementos com o mesmo comportamento na mesma página:

$("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 () é uma má ideia, pois quebra o comportamento padrão de muitas coisas, incluindo botões e links.

user2989143
fonte
Isso é ótimo, mas você pode simplificá-lo como$target.closest('#menu-container, #menu-activator').length === 0
Code Commander
5

Recentemente, enfrentei o mesmo problema. Escrevi o seguinte código:

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

Funcionou perfeitamente para mim.

Namish
fonte
4

que tal isso?

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

Acho mais útil usar o evento mousedown em vez do evento de clique. O evento de clique não funciona se o usuário clica em outros elementos da página com eventos de clique. Em combinação com o método one () da jQuery, tem a seguinte aparência:

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

Até eu me deparei com a mesma situação e um dos meus mentores expôs essa ideia para mim.

passo: 1 quando clicado no botão em que devemos mostrar o menu suspenso. em seguida, adicione o nome da classe abaixo "more_wrap_background" à página ativa atual como mostrado abaixo

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

etapa 2 e adicionar cliques para a tag div, como

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

onde hideDropDown é a função a ser chamada para ocultar o menu suspenso

A etapa 3 e a etapa importante ao ocultar o menu suspenso é remover aquela classe que você adicionou anteriormente, como

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

Estou removendo usando seu id no código acima

.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;
}
Abhimaan
fonte
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
    }
};

E melhor definir o ouvinte apenas quando o menu estiver visível e sempre remover o ouvinte após o menu ficar oculto.

Олег Всильдеревьев
fonte
1

Acho que você precisa de algo assim: 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();               
            }
      });
  });    
});

Espero que seja útil para você!

Sakata Gintoki
fonte
1

Use o seletor ': visible'. Onde .menuitem é o (s) elemento (s) a ocultar ...

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

Ou se você já tem o (s) elemento (s) .menuitem em uma var ...

var menitems = $('.menuitem');
$('body').click(function(){
  menuitems.filter(':visible').hide('fast');
});
Ekerner
fonte
0

Não precisa ser complicado.

$(document).on('click', function() {
    $("#menu:not(:hover)").hide();
});
Jonathan Gaudé
fonte