Eu tenho um campo de entrada que abre um menu suspenso personalizado. Eu gostaria da seguinte funcionalidade:
- Quando o usuário clica em qualquer lugar fora do campo de entrada, o menu deve ser removido.
- Se, mais especificamente, o usuário clicar em um div dentro do menu, o menu deve ser removido e um processamento especial deve ocorrer com base em qual div foi clicado.
Aqui está minha implementação:
O campo de entrada tem um onblur()
evento que exclui o menu (definindo o do pai innerHTML
como uma string vazia) sempre que o usuário clica fora do campo de entrada. Os divs dentro do menu também possuem onclick()
eventos que executam o processamento especial.
O problema é que os onclick()
eventos nunca disparam quando o menu é clicado, porque o campo de entrada onblur()
dispara primeiro e exclui o menu, incluindo o onclick()
s!
Eu resolvi o problema dividindo os divs Menu onclick()
em onmousedown()
e onmouseup()
eventos e definir um sinalizador global no mouse para baixo, que é cancelado no mouse para cima, semelhante ao que foi sugerido em esta resposta . Como onmousedown()
dispara antes onblur()
, o sinalizador será definido onblur()
se um dos divs do menu for clicado, mas não se em algum outro lugar da tela for clicado. Se o menu foi clicado, eu retorno imediatamente onblur()
sem excluí-lo e, em seguida, aguardo o onclick()
disparo, momento em que posso excluir o menu com segurança.
Existe uma solução mais elegante?
O código é parecido com este:
<div class="menu" onmousedown="setFlag()" onmouseup="doProcessing()">...</div>
<input id="input" onblur="removeMenu()" ... />
var mouseflag;
function setFlag() {
mouseflag = true;
}
function removeMenu() {
if (!mouseflag) {
document.getElementById('menu').innerHTML = '';
}
}
function doProcessing(id, name) {
mouseflag = false;
...
}
fonte
onmousedown
foi executado antesonblur
Testado no Firefox, Chrome e Internet Explorer.onMouseDown
comevent.preventDefault()
eonClick
com a ação desejada.onClick
não deve ser substituído poronMouseDown
.Embora essa abordagem funcione um pouco , os dois são eventos fundamentalmente diferentes que têm expectativas diferentes aos olhos do usuário. Usar em
onMouseDown
vez deonClick
arruinará a previsibilidade de seu software neste caso. Portanto, os dois eventos não são intercambiáveis.Para ilustrar: ao clicar acidentalmente em um botão, os usuários esperam conseguir manter o clique do mouse pressionado, arrastar o cursor para fora do elemento e soltar o botão do mouse, resultando em nenhuma ação.
onClick
faz isso.onMouseDown
não permite que o usuário mantenha o mouse pressionado e, em vez disso, aciona imediatamente uma ação, sem nenhum recurso para o usuário.onClick
é o padrão pelo qual esperamos acionar ações em um computador.Nesta situação, convoque
event.preventDefault()
oonMouseDown
evento.onMouseDown
causará um evento de desfoque por padrão e não o fará quandopreventDefault
for chamado. Então,onClick
terá a chance de ser chamado. Um evento de desfoque ainda vai acontecer, só depoisonClick
.Afinal, o
onClick
evento é uma combinação deonMouseDown
eonMouseUp
, se e somente se ambos ocorrerem no mesmo elemento.fonte
event.preventDefault()
noonMouseDown
evento, meuonFocus
evento não será chamadoSubstitua em
onmousedown
comonfocus
. Portanto, este evento será acionado quando o foco estiver dentro da caixa de texto.Substitua em
onmouseup
comonblur
. No momento em que você tirar o foco da caixa de texto, o onblur será executado.Eu acho que é disso que você precisa.
ATUALIZAÇÃO :
quando você executa sua função onfocus -> remove as classes que você irá aplicar em onblur e adicione as classes que você deseja que sejam executadas onfocus
e
quando você executa sua função onblur -> remove as classes que você irá aplicar no onfocus e adicione as classes que você deseja que sejam executadas no onblur
Não vejo nenhuma necessidade de variáveis de sinalização.
ATUALIZAÇÃO 2:
Você pode usar os eventos onmouseout e onmouseover
onmouseover -Detecta quando o cursor está sobre ele.
onmouseout -Detecta quando o cursor sai.
fonte
onmousedown()
eonmouseup()
no menu, não o campo de caixa de texto / entrada.Uma solução mais elegante (mas provavelmente com menos desempenho):
Em vez de usar o onblur da entrada para remover o menu, use
document.onclick
, que dispara depoisonblur
.No entanto, isso também significa que o menu é removido quando a própria entrada é clicada, o que é um comportamento indesejado. Defina um
input.onclick
comevent.stopPropagation()
para evitar a propagação de cliques para o evento de clique do documento.fonte
mudar onclick por onfocus
mesmo que onblur e onclick não se dêem muito bem, mas obviamente onfocus e sim onblur. já que mesmo após o menu ser fechado, o onfocus é válido para o elemento clicado dentro.
Eu fiz e funcionou.
fonte
Você pode usar uma
setInterval
função dentro do seuonBlur
manipulador, como esta:a
setInterval
função removerá suaonBlur
função da pilha de chamadas, adicione porque você definiu o tempo como 0, esta função será chamada imediatamente após a conclusão de outro manipulador de eventosfonte
setInterval
é uma má ideia, você nunca o cancela, então ele executará continuamente sua função e provavelmente fará com que seu site use max cpu. EmsetTimeout
vez disso, use se for usar esta técnica (que não recomendo). Fazer seu aplicativo depender do momento de certos eventos é um negócio arriscado.setTimeout
nãosetInterval
), mas tive que aumentá-la para250
ms antes que o tempo acontecesse na ordem correta. Este bug provavelmente ainda existirá em dispositivos mais lentos e também torna o atraso perceptível. Eu tentei oonMouseDown
e funciona bem. Eu me pergunto se ele precisa dos eventos de toque também.