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.



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


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

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

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.


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.

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

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.


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.


4

que tal isso?

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

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

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

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.


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ê!


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

0

Não precisa ser complicado.

$(document).on('click', function() {
    $("#menu:not(:hover)").hide();
});
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.