Estou procurando uma maneira de detectar se um evento de clique ocorreu fora de um componente, conforme descrito neste artigo . O jQuery closest () é usado para verificar se o destino de um evento click possui o elemento dom como um de seus pais. Se houver uma correspondência, o evento click pertence a um dos filhos e, portanto, não é considerado como estando fora do componente.
Então, no meu componente, quero anexar um manipulador de cliques à janela. Quando o manipulador é acionado, preciso comparar o alvo com os filhos do meu componente.
O evento click contém propriedades como "path", que parece conter o caminho dom que o evento percorreu. Não sei ao certo o que comparar ou a melhor forma de percorrê-lo, e acho que alguém já deve ter colocado isso em uma função inteligente de utilitário ... Não?
fonte
Respostas:
O uso de referências no React 16.3+ foi alterado.
A solução a seguir usa o ES6 e segue as práticas recomendadas para ligação, além de definir a ref por meio de um método.
Para vê-lo em ação:
Implementação de Classe:
Implementação de ganchos:
fonte
document.addEventListener
aqui?context
só é necessário como argumento no construtor se for usado. Não está sendo usado neste caso. A razão pela qual a equipe de reação recomenda terprops
como argumento o construtor é porque o uso dethis.props
antes da chamadasuper(props)
será indefinido e pode levar a erros.context
ainda está disponível nos nós internos, esse é o objetivo decontext
. Dessa forma, você não precisa transmiti-lo de componente para componente, como faz com os adereços. 2. Essa é apenas uma preferência estilística e não garante debate neste caso.Aqui está a solução que melhor funcionou para mim sem anexar eventos ao contêiner:
Certos elementos HTML podem ter o que é conhecido como " foco ", por exemplo, elementos de entrada. Esses elementos também responderão ao evento de desfoque , quando perderem o foco.
Para dar a qualquer elemento a capacidade de ter foco, verifique se o atributo tabindex está definido como algo diferente de -1. No HTML comum, isso seria definido pelo
tabindex
atributo, mas no React você deve usartabIndex
(observe a capitalI
).Você também pode fazer isso via JavaScript com
element.setAttribute('tabindex',0)
Era para isso que eu o estava usando, para criar um menu DropDown personalizado.
fonte
display:absolute
para que a lista suspensa não afete o tamanho da div principal. Isso significa que, quando clico em um item no menu suspenso, o onblur é acionado.onBlur
serão acionados no contêiner suspenso eexpanded
serão configurados como false.Eu estava preso no mesmo problema. Estou um pouco atrasado para a festa aqui, mas para mim esta é uma solução muito boa. Espero que ajude outra pessoa. Você precisa importar
findDOMNode
dereact-dom
Abordagem de reagir ganchos (16,8 +)
Você pode criar um gancho reutilizável chamado
useComponentVisible
.Em seguida, no componente que você deseja adicionar a funcionalidade, faça o seguinte:
Encontre um exemplo de codesandbox aqui.
fonte
ReactDOM.findDOMNode
e está obsoleto, deve usarref
retornos de chamada: github.com/yannickcr/eslint-plugin-react/issues/…Depois de experimentar muitos métodos aqui, decidi usar o github.com/Pomax/react-onclickoutside por causa de sua completude.
Instalei o módulo via npm e o importei para o meu componente:
Então, na minha classe de componente, defini o
handleClickOutside
método:E ao exportar meu componente, envolvi-o em
onClickOutside()
:É isso aí.
fonte
tabIndex
/onBlur
proposta por Pablo ? Como a implementação funciona e como seu comportamento difere daonBlur
abordagem?tabIndex/onBlur
abordagem. Não funciona quando o menu suspenso estiverposition:absolute
, como um menu pairando sobre outro conteúdo.Encontrei uma solução graças a Ben Alpert em discuss.reactjs.org . A abordagem sugerida anexa um manipulador ao documento, mas isso acabou sendo problemático. Clicar em um dos componentes da minha árvore resultou em um novo renderizador, que removeu o elemento clicado na atualização. Como o renderizador do React acontece antes da chamada do manipulador do corpo do documento, o elemento não foi detectado como "dentro" da árvore.
A solução para isso foi adicionar o manipulador no elemento raiz do aplicativo.
a Principal:
componente:
fonte
document
não estiver?mousedown
provavelmente seria um manipulador melhor do queclick
aqui. Na maioria dos aplicativos, o comportamento de fechar o menu clicando-fora ocorre no momento em que você passa o mouse, não quando você o libera. Experimente, por exemplo, com o sinalizador ou compartilhamento do Stack Overflow diálogos ou com um dos menus suspensos da barra de menus superior do navegador.ReactDOM.findDOMNode
e stringref
estão obsoletos, deve usarref
retornos de chamada: github.com/yannickcr/eslint-plugin-react/issues/…document
Implementação de hook baseada na excelente palestra de Tanner Linsley no JSConf Hawaii 2020 :
useOuterClick
API de ganchoImplementação
useOuterClick
faz uso de referências mutáveis para criar uma API lean para oClient
. É possível definir um retorno de chamada sem precisar memorizá-lo viauseCallback
. O corpo de retorno de chamada ainda tem acesso aos adereços e estado mais recentes (sem valores de fechamento obsoletos).Nota para usuários do iOS
O iOS trata apenas determinados elementos como clicáveis. Para contornar esse comportamento, escolha um ouvinte de clique externo diferente de
document
- nada mais, inclusivebody
. Por exemplo, você pode adicionar um ouvinte na raiz do Reactdiv
no exemplo acima e expandir sua altura (height: 100vh
ou similar) para capturar todos os cliques externos. Fontes: quirksmode.org e esta respostafonte
@media (hover: none) and (pointer: coarse) { body { cursor:pointer } }
Adicionando isso em meus estilos globais, parece que resolveu o problema.@supports (-webkit-overflow-scrolling: touch)
que visa apenas o IOS, embora eu não saiba mais quanto! Acabei de testar e funciona.Nenhuma das outras respostas aqui funcionou para mim. Eu estava tentando ocultar um pop-up no desfoque, mas como o conteúdo estava absolutamente posicionado, o onBlur estava disparando mesmo com o clique do conteúdo interno.
Aqui está uma abordagem que funcionou para mim:
Espero que isto ajude.
fonte
document
. Obrigado.[Update] Solução com React ^ 16.8 usando ganchos
CodeSandbox
Solução com React ^ 16.3 :
CodeSandbox
fonte
.contains is not a function
, pode ser porque você está passando o prop ref para um componente personalizado em vez de um elemento DOM real como um<div>
ref
prop a um componente personalizado, você pode querer ter um olhar paraReact.forwardRef
Aqui está minha abordagem (demo - https://jsfiddle.net/agymay93/4/ ):
Eu criei um componente especial chamado
WatchClickOutside
e pode ser usado como (eu assumoJSX
sintaxe):Aqui está o código do
WatchClickOutside
componente:fonte
ref
está obsoleta, deve usarref
retornos de chamada: reactjs.org/docs/refs-and-the-dom.htmlIsso já tem muitas respostas, mas elas não abordam
e.stopPropagation()
e impedem o clique nos links de reação fora do elemento que você deseja fechar.Devido ao fato de o React possuir seu próprio manipulador de eventos artificiais, você não pode usar o documento como base para os ouvintes de eventos. Você precisa
e.stopPropagation()
antes disso, pois o React usa o próprio documento. Se você usar, por exemplo, emdocument.querySelector('body')
vez disso. Você pode impedir o clique no link React. A seguir, é apresentado um exemplo de como eu implemento o clique fora e o fecho.Isso usa ES6 e React 16.3 .
fonte
Para aqueles que precisam de posicionamento absoluto, uma opção simples que eu optei é adicionar um componente de invólucro com estilo para cobrir a página inteira com um plano de fundo transparente. Em seguida, você pode adicionar um onClick nesse elemento para fechar seu componente interno.
Como agora, se você adicionar um manipulador de cliques no conteúdo, o evento também será propagado para a div superior e, portanto, acionará o manipuladorOutsideClick. Se esse não é o seu comportamento desejado, basta interromper o andamento do evento no seu manipulador.
`
fonte
Para estender a resposta aceita de Ben Bud , se você estiver usando componentes estilizados, passar refs dessa maneira resultará em um erro como "this.wrapperRef.contains não é uma função".
A correção sugerida, nos comentários, para envolver o componente estilizado com uma div e passar a referência para lá, funciona. Dito isto, em seus documentos eles já explicam a razão disso e o uso adequado de referências dentro de componentes denominados:
Igual a:
Então você pode acessá-lo diretamente na função "handleClickOutside":
Isso também se aplica à abordagem "onBlur":
fonte
A UI do material possui um pequeno componente para resolver esse problema: https://material-ui.com/components/click-away-listener/, que você pode escolher. Ele pesa 1,5 kB compactado, suporta dispositivos móveis, IE 11 e portais.
fonte
Minha maior preocupação com todas as outras respostas é ter que filtrar eventos de clique da raiz / pai para baixo. Achei que a maneira mais fácil era simplesmente definir um elemento irmão com a posição: fixed, um z-index 1 atrás da lista suspensa e manipular o evento click no elemento fixo dentro do mesmo componente. Mantém tudo centralizado em um determinado componente.
Código de exemplo
fonte
Evento
path[0]
é o último item clicadofonte
Eu usei este módulo (não tenho associação com o autor)
Ele fez o trabalho muito bem.
fonte
"Support for the experimental syntax 'classProperties' isn't currently enabled"
isso funcionou diretamente da caixa. Para quem estiver tentando esta solução, lembre-se de apagar qualquer código remanescente que você possa ter copiado ao tentar as outras soluções. por exemplo, adicionar ouvintes de eventos parece entrar em conflito com isso.Eu fiz isso em parte, seguindo isto e seguindo os documentos oficiais do React sobre o manuseio de refs, o que requer react ^ 16.3. Esta é a única coisa que funcionou para mim depois de tentar algumas das outras sugestões aqui ...
fonte
Um exemplo com estratégia
Gosto das soluções fornecidas que fazem a mesma coisa criando um wrapper em torno do componente.
Como esse é mais um comportamento, pensei em Estratégia e criei o seguinte.
Eu sou novo no React e preciso de um pouco de ajuda para salvar alguns clichês nos casos de uso
Por favor, revise e me diga o que acha.
ClickOutsideBehavior
Uso da amostra
Notas
Pensei em armazenar os
component
ganchos do ciclo de vida passados e substituí-los por métodos semelhantes a isso:component
é o componente passado ao construtor deClickOutsideBehavior
.Isso removerá o clichê de habilitação / desabilitação do usuário desse comportamento, mas não parece muito bom
fonte
No meu caso DROPDOWN, a solução do Ben Bud funcionou bem, mas eu tinha um botão de alternância separado com um manipulador onClick. Portanto, a lógica do clique externo entra em conflito com o botão onClick toggler. Aqui está como eu o resolvi passando a referência do botão também:
fonte
Se você deseja usar um componente minúsculo (466 Byte gzipped) que já existe para essa funcionalidade, consulte esta biblioteca react-outclick .
O bom da biblioteca é que ela também permite detectar cliques fora de um componente e dentro de outro. Ele também suporta a detecção de outros tipos de eventos.
fonte
Se você deseja usar um pequeno componente (466 Byte gzipped) que já existe para essa funcionalidade, consulte esta biblioteca react-outclick . Ele captura eventos fora de um componente de reação ou elemento jsx.
O bom da biblioteca é que ela também permite detectar cliques fora de um componente e dentro de outro. Ele também suporta a detecção de outros tipos de eventos.
fonte
Sei que essa é uma pergunta antiga, mas continuo me deparando com isso e tive muitos problemas para descobrir isso em um formato simples. Portanto, se isso facilitar a vida de alguém, use o OutsideClickHandler da airbnb. É o plug-in mais simples para realizar essa tarefa sem escrever seu próprio código.
Exemplo:
fonte
fonte
você pode uma maneira simples de resolver seu problema, eu mostro a você:
....
isso funciona para mim, boa sorte;)
fonte
Encontrei isso no artigo abaixo:
render () {return ({this.node = node;}}> alternar popover {this.state.popupVisible && (eu sou popover!)}); }}
Aqui está um ótimo artigo sobre esse problema: "Manipular cliques fora dos componentes do React" https://larsgraubner.com/handle-outside-clicks-react/
fonte
Adicione um
onClick
manipulador ao seu contêiner de nível superior e aumente um valor de estado sempre que o usuário clicar nele. Passe esse valor para o componente relevante e sempre que o valor mudar, você poderá trabalhar.Nesse caso, estamos ligando
this.closeDropdown()
sempre que oclickCount
valor muda.O
incrementClickCount
método é acionado dentro do.app
contêiner, mas não.dropdown
porque usamosevent.stopPropagation()
para impedir que o evento borbulhe.Seu código pode acabar parecido com isto:
fonte
Para que a solução 'focus' funcione para o menu suspenso com ouvintes de eventos, você pode adicioná-los com o evento onMouseDown em vez de onClick . Dessa forma, o evento será acionado e depois o pop-up será fechado da seguinte maneira:
fonte
fonte
import ReactDOM from 'react-dom';
Eu fiz uma solução para todas as ocasiões.
Você deve usar um componente de alta ordem para agrupar o componente que deseja ouvir cliques fora dele.
Este exemplo de componente possui apenas um suporte: "onClickedOutside" que recebe uma função.
fonte
UseOnClickOutside Hook - Reagir 16.8 +
Criar uma função useOnOutsideClick geral
Em seguida, use o gancho em qualquer componente funcional.
Link para demonstração
Parcialmente inspirado pela resposta @ pau1fitzgerald.
fonte