Comment désactiver les événements mouseout déclenchés par des éléments enfants?

107

Laissez-moi décrire le problème en détail:

Je souhaite afficher un div positionné en absolu lors du survol d'un élément. C'est vraiment simple avec jQuery et fonctionne très bien. Mais lorsque la souris passe sur l'un des éléments enfants, elle déclenche l'événement mouseout du div contenant. Comment empêcher javascript de déclencher l'événement mouseout de l'élément conteneur lors du survol d'un élément enfant.

Quel est le moyen le meilleur et le plus court pour faire cela avec jQuery?

Voici un exemple simplifié pour illustrer ce que je veux dire:

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() } );
Ponceuse Versluys
la source
1
Sans la version jQuery de cette question
Zach Saucier

Réponses:

209

La question est un peu ancienne, mais je l'ai rencontrée l'autre jour.

Le moyen le plus simple de le faire avec les versions récentes de jQuery est d'utiliser les événements mouseenteret mouseleaveplutôt que mouseoveret mouseout.

Vous pouvez tester le comportement rapidement avec:

$(".myClass").on( {
   'mouseenter':function() { console.log("enter"); },
   'mouseleave':function() { console.log("leave"); }
});
bytebrite
la source
4
J'ai aidé mon problème lié au javascript non jquery. J'utilisais mouseenter / mouseout au lieu de mouseleave avec mes écouteurs d'événements!
Jo E.
1
Pour la recherche, ces comportements d'événement de souris sont équivalents à ROLL_OVERet ROLL_OUTdans AS3.
Jeff Ward le
2
M'a sauvé des maux de tête. Il est étrange de voir comment le survol des éléments enfants déclenche l' mouseoutévénement du parent , alors qu'en fait, il est toujours à l'intérieur de l'élément parent.
javiniar.leonard
18

Par souci de simplicité, je réorganiserais juste un peu le html pour placer le contenu nouvellement affiché dans l'élément auquel l'événement mouseover est lié:

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

Ensuite, vous pouvez faire quelque chose comme ceci:

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

Remarque: je ne recommande pas le css en ligne, mais cela a été fait pour rendre l'exemple plus facile à digérer.

Ryan McGeary
la source
C'est en effet une solution très simple et propre. Merci de me le rappeler. Mais dans ma situation spécifique, qui n'est pas exactement comme dans la question, ce n'est pas une option. Merci quand même!
Sander Versluys
Après avoir essayé différentes méthodes décrites par d'autres ici à SO, je suis revenu à votre méthode et je l'ai fait fonctionner dans mon cas. Yay! :-)
Sander Versluys
11

Oui, les gars, utilisez à la .mouseleaveplace de .mouseout:

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

Ou même utilisez-le en combinaison avec live:

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

Vous recherchez l'équivalent jQuery du javascript de prévention des bulles d'événement.

Regarde ça:

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

Fondamentalement, vous devez capturer l'événement dans les nœuds DOM enfants, et arrêter leur propagation dans l'arborescence DOM. Une autre méthode, bien que non suggérée (car elle peut gravement perturber les événements existants sur votre page), consiste à définir la capture d'événements sur un élément spécifique de la page, et elle recevra tous les événements. Ceci est utile pour le comportement DnD et autres, mais certainement pas pour votre cas.

Yuval Adam
la source
cela fonctionne comme il se doit, je ne sais pas pourquoi vous ne pouvez pas le faire fonctionner .. juste une info, un lien vers la documentation a changé, le nouveau lien est docs.jquery.com/Events/jQuery.Event#event.stopPropagation. 28.29
zappan
3

Je vérifie simplement si les coordonnées de la souris sont en dehors de l'élément dans l'événement mouseout.

Cela fonctionne mais c'est beaucoup de code pour une chose aussi 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
    }
}

Le code réduit pour la lisibilité, ne fonctionnera pas hors de la boîte.


la source
1

Je suis d'accord avec Ryan.

Votre problème est que le div "suivant" n'est pas un "enfant" de A. Il n'y a aucun moyen pour HTML ou jQuery de savoir que votre élément "a" est lié au div enfant dans le DOM. Les envelopper et placer un survol sur le wrapper signifie qu'ils sont associés.

Veuillez noter que son code n'est cependant pas conforme aux meilleures pratiques. Ne définissez pas le style caché en ligne sur les éléments; si l'utilisateur a CSS mais pas javascript, l'élément se cachera et ne pourra pas être affiché. La meilleure pratique consiste à mettre cette déclaration dans l'événement document.ready.

utilisateur37731
la source
Je suis tout à fait d'accord pour mettre le hide dans l'événement document.ready. Le css en ligne était juste pour donner un exemple de travail complet de la manière la plus simple possible. Je vais modifier ma réponse pour que cela soit clair.
Ryan McGeary
1

J'ai résolu ce problème en ajoutant pointer-events: none;sur mes éléments enfants css

Antoine
la source
Cela a vraiment beaucoup aidé lors de la correction d'un code hérité en créant ses propres popovers avec des événements mouseover et mouseleave
CM
0

La façon dont j'ai généralement vu cela géré est d'avoir un délai d'environ 1/2 seconde entre le déplacement de la souris de l'élément HoverMe. Lorsque vous déplacez la souris dans l'élément survolé, vous voudrez définir une variable indiquant que vous survolez l'élément, puis arrêter fondamentalement la partie survolée de se cacher si cette variable est définie. Vous devez ensuite ajouter une fonction de masquage similaire OnMouseOut à partir de l'élément survolé pour le faire disparaître lorsque vous retirez la souris. Désolé, je ne peux pas vous donner de code ou quelque chose de plus concret, mais j'espère que cela vous oriente dans la bonne direction.

Kibbee
la source
Oui idd je viens de mettre en place une solution comme celle-là. C'est un problème très courant. Je suis vraiment curieux de savoir quelle autre solution existe ... Merci pour la réponse!
Sander Versluys
0

C'est une vieille question, mais elle ne devient jamais obsolète. La bonne réponse doit être celle donnée par bytebrite .

Je voudrais seulement souligner la différence entre mouseover / mouseout et mouseenter / mouseleave. Vous pouvez lire une explication intéressante et utile ici (allez tout en bas de la page pour une démo fonctionnelle). Lorsque vous utilisez mouseout, l'événement s'arrête lorsque la souris entre dans un autre élément, même s'il s'agit d'un élément enfant. De l'autre côté, lorsque vous utilisez mouseleave, l'événement n'est pas déclenché lorsque la souris survole un élément enfant, et c'est le comportement que l'OP souhaite obtenir.

Erenor Paz
la source