Como desabilitar eventos de mouseout disparados por elementos filho?

107

Deixe-me descrever o problema em detalhes:

Quero mostrar uma div posicionada absoluta ao passar o mouse sobre um elemento. Isso é muito simples com jQuery e funciona muito bem. Mas quando o mouse passa sobre um dos elementos filho, ele aciona o evento mouseout do div que o contém. Como evito que o javascript acione o evento mouseout do elemento que o contém ao passar o mouse sobre um elemento filho.

Qual é a melhor e mais curta maneira de fazer isso com jQuery?

Aqui está um exemplo simplificado para ilustrar o que quero dizer:

Html:

<a>Hover Me</a>
<div>
  <input>Test</input>
  <select>
    <option>Option 1</option>
    <option>Option 2</option>
  </select>
</div>

Javascript / jQuery:

$('a').hover( function() { $(this).next().show() }
              function() { $(this).next().hide() } );
Sander Versluys
fonte
1
Sem a versão jQuery desta pergunta
Zach Saucier

Respostas:

209

A pergunta é um pouco antiga, mas me deparei com isso outro dia.

A maneira mais simples de fazer isso com versões recentes do jQuery é usar os eventos mouseentere em mouseleavevez de mouseovere mouseout.

Você pode testar o comportamento rapidamente com:

$(".myClass").on( {
   'mouseenter':function() { console.log("enter"); },
   'mouseleave':function() { console.log("leave"); }
});
bytebrite
fonte
4
Ajudou no meu problema não relacionado ao javascript jquery. Eu estava usando mouseenter / mouseout em vez de mouseleave com meus ouvintes de eventos!
Jo E.
1
Para fins de pesquisa, esses comportamentos de evento do mouse são equivalentes a ROLL_OVERe ROLL_OUTno AS3.
Jeff Ward,
2
Me salvou de algumas dores de cabeça. É estranho como pairar sobre os elementos filhos dispara o mouseoutevento dos pais , quando na verdade ele ainda está dentro do elemento pai.
javiniar.leonard
18

Para simplificar, eu apenas reorganizaria o html um pouco para colocar o conteúdo recém-exibido dentro do elemento ao qual o evento mouseover está vinculado:

<div id="hoverable">
  <a>Hover Me</a>
  <div style="display:none;">
    <input>Test</input>
    <select>
      <option>Option 1</option>
      <option>Option 2</option>
    </select>
  </div>
</div>

Então, você poderia fazer algo assim:

$('#hoverable').hover( function() { $(this).find("div").show(); },
                       function() { $(this).find("div").hide(); } );

Observação: não recomendo css inline, mas foi feito para tornar o exemplo mais fácil de digerir.

Ryan McGeary
fonte
Esta é realmente uma solução muito simples e limpa. Obrigado por me lembrar. Mas na minha situação específica, que não é exatamente como na pergunta, essa não é uma opção. Obrigado!
Sander Versluys
Depois de tentar métodos diferentes, conforme descrito por outros aqui no SO, voltei ao seu método e o fiz funcionar no meu caso. Yay! :-)
Sander Versluys
11

Sim, pessoal, use em .mouseleavevez de .mouseout:

$('div.sort-selector').mouseleave(function() {
    $(this).hide();
});

Ou até mesmo usá-lo em combinação com live:

$('div.sort-selector').live('mouseleave', function() {
    $(this).hide();
});
Lord Nighton
fonte
8

Você está procurando o equivalente em jQuery do javascript de prevenção de bolhas de eventos.

Veja isso:

http://docs.jquery.com/Events/jQuery.Event#event.stopPropagation.28.29

Basicamente, você precisa capturar o evento nos nós DOM filhos e, assim, parar sua propagação na árvore DOM. Outra maneira, embora realmente não seja sugerida (já que pode atrapalhar seriamente os eventos existentes em sua página), é definir a captura de eventos para um elemento específico na página, e ela receberá todos os eventos. Isso é útil para o comportamento DnD e tal, mas definitivamente não para o seu caso.

Yuval Adam
fonte
isso funciona como deveria, não sei porque você não conseguiu fazê-lo funcionar .. apenas uma informação, um link para documentos mudou, o novo link é docs.jquery.com/Events/jQuery.Event#event.stopPropagation. 28,29
zappan
3

Estou simplesmente verificando se as coordenadas do mouse estão fora do elemento no evento mouseout.

Funciona, mas é muito código para uma coisa tão simples :(

function mouseOut(e)
{
    var pos = GetMousePositionInElement(e, element);
    if (pos.x < 0 || pos.x >= element.size.X || pos.y < 0 || pos.y >= element.size.Y)
    {
        RealMouseOut();
    }
    else
    {
         //Hit a child-element
    }
}

Código reduzido para legibilidade, não funcionará fora da caixa.


fonte
1

Eu concordo com o Ryan.

Seu problema é que o "próximo" div não é um "filho" de A. Não há como HTML ou jQuery saber que seu elemento "a" está relacionado ao div filho no DOM. Envolvê-los e colocar um foco no invólucro significa que eles estão associados.

No entanto, observe que seu código não está de acordo com as práticas recomendadas. Não defina o estilo oculto embutido nos elementos; se o usuário tiver CSS, mas não javascript, o elemento ficará oculto e não poderá ser mostrado. A melhor prática é colocar essa declaração no evento document.ready.

user37731
fonte
Eu concordo totalmente em colocar o hide no evento document.ready. O css inline servia apenas para transmitir um exemplo funcional completo da maneira mais simples possível. Vou editar minha resposta para deixar isso claro.
Ryan McGeary
1

Resolvi esse problema adicionando pointer-events: none;meu css de elementos filho

Antoine
fonte
Isso realmente ajudou muito na correção de alguns códigos legados criando seus próprios popovers com eventos mouseover e mouseleave
CM
0

Normalmente, vejo isso sendo tratado com um atraso de cerca de 1/2 segundo entre mover o mouse do elemento HoverMe. Ao mover o mouse para o elemento pairado, você deseja definir alguma variável que sinalize que você está passando o mouse sobre o elemento e, em seguida, basicamente, impedir que a parte pairada se esconda se essa variável for definida. Em seguida, você deve adicionar uma função de ocultar semelhante OnMouseOut do elemento pairado para fazê-lo desaparecer quando você remove o mouse. Desculpe, não posso fornecer nenhum código ou algo mais concreto, mas espero que isso aponte a direção certa.

Kibee
fonte
Sim idd Acabei de implementar uma solução como essa. É um problema muito comum. Estou realmente curioso para saber que outra solução existe ... Obrigado pela resposta!
Sander Versluys
0

É uma pergunta antiga, mas nunca fica obsoleta. A resposta correta deve ser a fornecida por bytebrite .

Gostaria apenas de apontar a diferença entre mouseover / mouseout e mouseenter / mouseleave. Você pode ler uma explicação excelente e útil aqui (vá até o final da página para ver uma demonstração funcional). Quando você usa mouseout, o evento para quando o mouse entra em outro elemento, mesmo que seja um elemento filho. Por outro lado, quando você usa mouseleave, o evento não é disparado quando o mouse passa sobre um elemento filho, e este é o comportamento que o OP gostaria de obter.

Erenor Paz
fonte