Eu tenho alguns menus HTML, que mostro completamente quando um usuário clica no cabeçalho desses menus. Gostaria de ocultar esses elementos quando o usuário clica fora da área de menus.
É possível algo assim com o jQuery?
$("#menuscontainer").clickOutsideThisElement(function() {
// Hide the menus
});
javascript
jquery
click
Sergio del Amo
fonte
fonte
event.target
e semevent.stopPropagation
.Respostas:
Anexe um evento de clique ao corpo do documento que fecha a janela. Anexe um evento de clique separado ao contêiner que interrompe a propagação no corpo do documento.
fonte
$('html').click()
não deve usar o corpo. O corpo sempre tem a altura do seu conteúdo. Não há muito conteúdo ou a tela é muito alta, funciona apenas na parte preenchida pelo corpo.Você pode ouvir um evento de clique
document
e, em seguida, verifique se#menucontainer
não é um ancestral ou o destino do elemento clicado usando.closest()
.Caso contrário, o elemento clicado está fora do
#menucontainer
e você pode ocultá-lo com segurança.Editar - 23-06-2017
Você também pode limpar após o ouvinte de eventos se planejar descartar o menu e desejar parar de ouvir os eventos. Esta função limpará apenas o ouvinte recém-criado, preservando outros ouvintes de clique
document
. Com a sintaxe do ES2015:Editar - 2018-03-11
Para quem não quer usar o jQuery. Aqui está o código acima em plain vanillaJS (ECMAScript6).
NOTA: Isso é baseado no comentário de Alex para usar apenas em
!element.contains(event.target)
vez da parte do jQuery.Mas
element.closest()
agora também está disponível em todos os principais navegadores (a versão W3C difere um pouco da jQuery). Os polyfills podem ser encontrados aqui: Element.closest ()Edição - 2020-05-21
No caso em que você deseja que o usuário possa clicar e arrastar dentro do elemento, solte o mouse fora do elemento, sem fechar o elemento:
E em
outsideClickListener
:fonte
!element.contains(event.target)
usando Node.contains ()A razão pela qual essa pergunta é tão popular e tem tantas respostas é que ela é enganosamente complexa. Depois de quase oito anos e dezenas de respostas, estou genuinamente surpreso ao ver quão pouco cuidado foi dado à acessibilidade.
Esta é uma causa nobre e é o problema real . O título da pergunta - que é o que a maioria das respostas parece tentar resolver - contém um infeliz arenque vermelho.
Dica: é a palavra "clique" !
Na verdade, você não deseja vincular manipuladores de cliques.
Se você está vinculando manipuladores de clique para fechar a caixa de diálogo, você já falhou. O motivo pelo qual você falhou é que nem todos acionam
click
eventos. Os usuários que não usarem o mouse poderão escapar da sua caixa de diálogo (e seu menu pop-up é sem dúvida um tipo de caixa de diálogo) pressionando Tabe, em seguida, não poderão ler o conteúdo por trás da caixa de diálogo sem disparar umclick
evento posteriormente .Então, vamos reformular a pergunta.
Esse é o objetivo. Infelizmente, agora precisamos vincular o
userisfinishedwiththedialog
evento, e essa vinculação não é tão direta.Então, como podemos detectar que um usuário terminou de usar uma caixa de diálogo?
focusout
eventoUm bom começo é determinar se o foco saiu da caixa de diálogo.
Dica: tenha cuidado com o
blur
evento,blur
não se propaga se o evento foi vinculado à fase de bolhas!jQuery
focusout
vai fazer muito bem. Se você não pode usar o jQuery, pode usarblur
durante a fase de captura:Além disso, em muitas caixas de diálogo, você precisará permitir que o contêiner ganhe foco. Adicionar
tabindex="-1"
para permitir que o diálogo receba foco dinamicamente sem interromper o fluxo de tabulação.Se você jogar com essa demonstração por mais de um minuto, deve começar a ver rapidamente os problemas.
A primeira é que o link na caixa de diálogo não é clicável. Tentar clicar nele ou na guia dele levará ao fechamento do diálogo antes que a interação ocorra. Isso ocorre porque o foco no elemento interno aciona um
focusout
evento antes de acioná-lofocusin
novamente.A correção é enfileirar a alteração de estado no loop de eventos. Isso pode ser feito usando
setImmediate(...)
ousetTimeout(..., 0)
para navegadores que não suportamsetImmediate
. Uma vez na fila, ele pode ser cancelado por um subseqüentefocusin
:Mostrar snippet de código
A segunda questão é que a caixa de diálogo não será fechada quando o link for pressionado novamente. Isso ocorre porque a caixa de diálogo perde o foco, acionando o comportamento de fechamento, após o qual o clique no link aciona a caixa de diálogo para reabrir.
Semelhante à edição anterior, o estado do foco precisa ser gerenciado. Dado que a mudança de estado já foi enfileirada, é apenas uma questão de lidar com eventos de foco nos gatilhos da caixa de diálogo:
Isso deve parecer familiarMostrar snippet de código
Esc chave
Se você pensou que tinha terminado lidando com os estados de foco, há mais a fazer para simplificar a experiência do usuário.
Geralmente, é um recurso "bom de ter", mas é comum que, quando você tem um modal ou pop-up de qualquer tipo, a Escchave o feche.
Mostrar snippet de código
Se você souber que possui elementos de foco na caixa de diálogo, não precisará focar a caixa de diálogo diretamente. Se você estiver criando um menu, poderá focar o primeiro item do menu.
Mostrar snippet de código
Funções WAI-ARIA e outro suporte à acessibilidade
Esperamos que esta resposta cubra o básico do suporte acessível de teclado e mouse para esse recurso, mas como já é bastante considerável, evitarei qualquer discussão sobre as funções e atributos do WAI-ARIA , no entanto, recomendo vivamente que os implementadores consultem a especificação para obter detalhes sobre quais funções eles devem usar e quaisquer outros atributos apropriados.
fonte
As outras soluções aqui não funcionaram para mim, então tive que usar:
fonte
&& !$(event.target).parents("#foo").is("#foo")
dentro daIF
instrução para que quaisquer elementos filhos não fechem o menu quando clicados..is('#foo, #foo *')
, mas eu não recomendo manipuladores de clique de ligação para resolver esse problema .!$(event.target).closest("#foo").length
seria melhor e evita a necessidade da adição de @ honyovk.Eu tenho um aplicativo que funciona de maneira semelhante ao exemplo de Eran, exceto que eu anexo o evento click ao corpo quando abro o menu ... Mais ou menos assim:
Mais informações sobre a
one()
função do jQueryfonte
.one
- dentro do$('html')
manipulador - escreva a$('html').off('click')
.one
manipulador chamará automaticamenteoff
(como é mostrado nos documentos do jQuery).Após a pesquisa, encontrei três soluções de trabalho (esqueci os links da página para referência)
Primeira solução
Segunda solução
Terceira solução
fonte
document.getElementsByClassName
, se alguém tiver uma idéia, compartilhe.Funciona para mim muito bem.
fonte
#menucontainer
que a pergunta era sobre um cliquetabindex="-1"
ao#menuscontainer
para fazê-lo funcionar. Parece que se você colocar uma tag de entrada dentro do contêiner e clicar nele, o contêiner fica oculto.Agora existe um plugin para isso: eventos externos ( postagem no blog )
Acontece o seguinte quando um clickoutside manipulador (WLOG) está ligada a um elemento:
Portanto, nenhum evento é interrompido da propagação e manipuladores de clique adicionais podem ser usados "acima" do elemento com o manipulador externo.
fonte
$( '#element' ).on( 'clickoutside', function( e ) { .. } );
Isso funcionou para mim perfeitamente !!
fonte
Não acho que você realmente precise fechar o menu quando o usuário clicar fora; o que você precisa é que o menu seja fechado quando o usuário clicar em qualquer lugar da página. Se você clicar no menu ou sair do menu, ele deve fechar, certo?
Não encontrar respostas satisfatórias acima me levou a escrever este post no outro dia. Para os mais pedantes, há várias dicas a serem observadas:
body { margin-left:auto; margin-right: auto; width:960px;}
fonte
Como outro pôster disse, existem muitas dicas, especialmente se o elemento que você está exibindo (neste caso, um menu) possui elementos interativos. Eu achei o seguinte método bastante robusto:
fonte
Uma solução simples para a situação é:
O script acima ocultará o evento
div
fora dodiv
clique é acionado.Você pode ver o seguinte blog para obter mais informações: http://www.codecanal.com/detect-click-outside-div-using-javascript/
fonte
Solução1
Em vez de usar event.stopPropagation () que pode ter alguns efeitos colaterais, basta definir uma variável de flag simples e adicionar uma
if
condição. Eu testei isso e funcionou corretamente, sem nenhum efeito colateral do stopPropagation:Solução2
Com apenas uma
if
condição simples :fonte
Verifique o destino do evento de clique na janela (ele deve se propagar para a janela, desde que não seja capturado em nenhum outro lugar) e verifique se ele não é um dos elementos do menu. Caso contrário, você está fora do seu menu.
Ou verifique a posição do clique e veja se ele está contido na área do menu.
fonte
Eu tive sucesso com algo assim:
A lógica é: quando
#menuscontainer
mostrado, vincule um manipulador de cliques ao corpo que oculta#menuscontainer
apenas se o destino (do clique) não for filho dele.fonte
Como uma variante:
Não há nenhum problema em interromper a propagação de eventos e suporta melhor vários menus na mesma página, onde clicar em um segundo menu enquanto um primeiro é aberto deixará o primeiro aberto na solução stopPropagation.
fonte
O evento possui uma propriedade chamada event.path do elemento, que é uma "lista ordenada estática de todos os seus ancestrais em ordem de árvore" . Para verificar se um evento se originou de um elemento DOM específico ou de um de seus filhos, basta verificar o caminho para esse elemento DOM específico. Também pode ser usado para verificar vários elementos, logicamente
OR
a verificação de elemento nasome
função.Então, para o seu caso, deve ser
fonte
event.path
não é uma coisa.Eu encontrei esse método em algum plugin de calendário jQuery.
fonte
Aqui está a solução de baunilha JavaScript para futuros espectadores.
Ao clicar em qualquer elemento do documento, se o ID do elemento clicado for alternado ou o elemento oculto não estiver oculto e o elemento oculto não contiver o elemento clicado, alterne o elemento.
Mostrar snippet de código
Se você tiver várias alternâncias na mesma página, poderá usar algo como isto:
hidden
ao item recolhível.fonte
Estou surpreso que ninguém realmente reconheceu o
focusout
evento:fonte
Se você estiver criando scripts para o IE e o FF 3. * e quiser apenas saber se o clique ocorreu em uma determinada área da caixa, você também pode usar algo como:
fonte
Em vez de usar interrupção de fluxo, evento de desfoque / foco ou qualquer outra técnica complicada, basta combinar o fluxo de eventos com o parentesco do elemento:
Para remover o clique fora do ouvinte de evento, basta:
fonte
#menuscontainer
você ainda está passando por seus pais. você deve primeiro verificar isso e, se não for esse elemento, suba na árvore do DOM.if(!($(event.target).is("#menuscontainer") || $(event.target).parents().is("#menuscontainer"))){
. É uma otimização pequena, mas ocorre apenas algumas vezes na vida útil do programa: para cada clique, se oclick.menu-outside
evento estiver registrado. É mais longo (+32 caracteres) e não usar o método de encadeamentoUsar:
fonte
Se alguém curioso aqui é a solução javascript (es6):
e es5, apenas no caso de:
});
fonte
Aqui está uma solução simples por javascript puro. Está atualizado com o ES6 :
fonte
() => {}
vez defunction() {}
. O que você tem lá é classificado como JavaScript simples com um toque do ES6.Conecte um ouvinte de evento de clique no documento. Dentro do ouvinte de evento, você pode olhar para o objeto de evento , em particular o event.target para ver em qual elemento foi clicado:
fonte
Eu usei abaixo script e feito com jQuery.
Abaixo encontre o código HTML
Você pode ler o tutorial aqui
fonte
Se você clicar no documento, oculte um determinado elemento, a menos que você clique no mesmo elemento.
fonte
Voto positivo para a resposta mais popular, mas adicione
portanto, um clique em uma barra de rolagem não [oculta ou o que seja] seu elemento de destino.
fonte
Para um uso mais fácil e um código mais expressivo, criei um plugin jQuery para isso:
Nota: target é o elemento em que o usuário realmente clicou. Mas o retorno de chamada ainda é executado no contexto do elemento original, para que você possa utilizá- lo conforme o esperado em um retorno de chamada do jQuery.
Plugar:
Por padrão, o ouvinte de evento de clique é colocado no documento. No entanto, se você deseja limitar o escopo do ouvinte de eventos, pode transmitir um objeto jQuery que representa um elemento de nível pai que será o pai principal no qual os cliques serão ouvidos. Isso evita ouvintes de eventos desnecessários no nível do documento. Obviamente, ele não funcionará, a menos que o elemento pai fornecido seja um pai do seu elemento inicial.
Use assim:
fonte