¿Cómo deshabilitar los eventos de mouseout activados por elementos secundarios?

107

Permítanme describir el problema en detalle:

Quiero mostrar un div posicionado absoluto al pasar el cursor sobre un elemento. Eso es realmente simple con jQuery y funciona bien. Pero cuando el mouse pasa sobre uno de los elementos secundarios, desencadena el evento mouseout del div contenedor. ¿Cómo evito que javascript active el evento mouseout del elemento contenedor cuando coloco el cursor sobre un elemento secundario?

¿Cuál es la forma mejor y más corta de hacer eso con jQuery?

Aquí hay un ejemplo simplificado para ilustrar lo que quiero decir:

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() } );
Lijadora Versluys
fuente
1
Sin la versión jQuery de esta pregunta
Zach Saucier

Respuestas:

209

La pregunta es un poco vieja, pero me encontré con esto el otro día.

La forma más sencilla de hacer esto con las versiones recientes de jQuery es usar los eventos mouseentery en mouseleavelugar de mouseovery mouseout.

Puede probar el comportamiento rápidamente con:

$(".myClass").on( {
   'mouseenter':function() { console.log("enter"); },
   'mouseleave':function() { console.log("leave"); }
});
bytebrite
fuente
4
Ayudó a mi problema relacionado con javascript no jquery. ¡Estaba usando mouseenter / mouseout en lugar de mouseleave con mis oyentes de eventos!
Jo E.
1
Por el amor de búsqueda, estos comportamientos de eventos de ratón son equivalentes a ROLL_OVERy ROLL_OUTen AS3.
Jeff Ward
2
Me salvó algunos dolores de cabeza. Es extraño cómo al pasar el cursor sobre los elementos secundarios se activa el mouseoutevento del padre , cuando en realidad todavía está dentro del elemento principal.
javiniar.leonard
18

En aras de la simplicidad, simplemente reorganizaría un poco el html para poner el contenido recién mostrado dentro del elemento al que está vinculado el evento mouseover:

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

Entonces, podrías hacer algo como esto:

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

Nota: No recomiendo CSS en línea, pero se hizo para que el ejemplo sea más fácil de digerir.

Ryan McGeary
fuente
De hecho, esta es una solución muy simple y limpia. Gracias por recordarme. Pero en mi situación específica, que no es exactamente como en la pregunta, esta no es una opción. Pero gracias!
Sander Versluys
Después de probar diferentes métodos descritos por otros aquí en SO, volví a su método y lo hice funcionar en mi caso. ¡Hurra! :-)
Sander Versluys
11

Sí, chicos, use en .mouseleavelugar de .mouseout:

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

O incluso úselo en combinación con live:

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

Está buscando el equivalente en jQuery de la función de prevención de eventos de javascript.

Mira esto:

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

Básicamente, necesita capturar el evento en los nodos DOM secundarios y detener su propagación hacia arriba en el árbol DOM. Otra forma, aunque en realidad no se sugiere (ya que puede afectar gravemente a los eventos existentes en su página), es configurar la captura de eventos en un elemento específico de la página, y recibirá todos los eventos. Esto es útil para el comportamiento de DnD y demás, pero definitivamente no para su caso.

Yuval Adam
fuente
esto funciona como debería, no sé por qué no pudo hacerlo funcionar ... solo una información, un enlace a documentos ha cambiado, el nuevo enlace es docs.jquery.com/Events/jQuery.Event#event.stopPropagation. 28.29
zappan
3

Simplemente estoy comprobando si las coordenadas del mouse están fuera del elemento en el evento mouseout.

Funciona pero hay mucho código para algo tan simple :(

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 reducido para legibilidad, no funcionará de inmediato.


fuente
1

Estoy de acuerdo con Ryan.

Su problema es que el "siguiente" div no es un "hijo" de A. No hay forma de que HTML o jQuery sepan que su elemento "a" está relacionado con el div hijo en el DOM. Envolverlos y colocar el cursor sobre el contenedor significa que están asociados.

Sin embargo, tenga en cuenta que su código no se ajusta a las mejores prácticas. No establezca el estilo oculto en línea en los elementos; si el usuario tiene CSS pero no javascript, el elemento se ocultará y no se podrá mostrar. Una mejor práctica es poner esa declaración en el evento document.ready.

user37731
fuente
Estoy totalmente de acuerdo con poner el hide en el evento document.ready. El CSS en línea fue solo para transmitir un ejemplo completo de trabajo de la manera más simple posible. Editaré mi respuesta para que quede claro.
Ryan McGeary
1

Resolví este problema agregando pointer-events: none;mis elementos secundarios css

Antoine
fuente
Esto realmente ayudó mucho cuando se corrigió un código heredado que construyó sus propios popovers con eventos de mouseover y mouseleave
CM
0

La forma en que generalmente he visto esto manejado es tener un retraso de aproximadamente 1/2 segundo entre mover el mouse desde el elemento HoverMe. Al mover el mouse hacia el elemento flotante, querrá establecer alguna variable que indique que está flotando sobre el elemento, y luego básicamente evitar que la parte flotante se oculte si esta variable está configurada. Luego, debe agregar una función de ocultar similar, OnMouseOut, del elemento flotante para que desaparezca cuando retire el mouse. Lo siento, no puedo darte ningún código, o algo más concreto, pero espero que esto te oriente en la dirección correcta.

Kibbee
fuente
Sí, idd, acabo de implementar una solución como esa. Es un problema muy común. Tengo mucha curiosidad por saber qué otras soluciones hay ... ¡Gracias por la respuesta!
Sander Versluys
0

Es una vieja pregunta, pero nunca se vuelve obsoleta. La respuesta correcta debe ser la dada por bytebrite .

Solo me gustaría señalar la diferencia entre mouseover / mouseout y mouseenter / mouseleave. Puede leer una excelente y útil explicación aquí (vaya al final de la página para ver una demostración funcional). Cuando lo usa mouseout, el evento se detiene cuando el mouse ingresa a otro elemento, incluso si es un elemento secundario. Por otro lado, cuando lo usa mouseleave, el evento no se activa cuando el mouse pasa por encima de un elemento secundario, y este es el comportamiento que le gustaría lograr al OP.

Erenor Paz
fuente