Comment vérifier si la souris survole un élément dans jQuery?

265

Existe-t-il un moyen rapide et facile de le faire dans jQuery qui me manque?

Je ne veux pas utiliser l'événement mouseover car je l'utilise déjà pour autre chose. J'ai juste besoin de savoir si la souris survole un élément à un moment donné.

J'aimerais faire quelque chose comme ça, si seulement il y avait une fonction "IsMouseOver":

function hideTip(oi) {
    setTimeout(function() { if (!IsMouseOver(oi)) $(oi).fadeOut(); }, 100);
}
Steve Wortham
la source
5
Dans la plupart des cas, les réponses données sont suffisantes, mais il y a des cas où le mousein / out n'est pas suffisant. Par exemple, masquer un menu lorsque la souris n'est plus sur la tête du menu OU sur le corps du menu.
Marcus Downing
J'ai utilisé la méthode décrite dans ma réponse pour travailler avec des icônes (événements de souris pour les bordures de boutons) qui ouvrent des listes déroulantes animées à fermeture retardée. Vous gérez le délai de retard / annulation dans l'icône et le menu déroulant en utilisant les méthodes triggerHandler de jquery. Entièrement suffisant.
mothmonsterman
#Marcus: si vous masquez un menu, quelle est la meilleure façon de procéder?
coderama
J'aurais voté contre si la meilleure réponse avait été marquée comme la solution.
BBaysinger

Réponses:

97

Définissez un délai d'expiration sur la souris pour un fondu et stockez la valeur de retour aux données dans l'objet. Puis à la souris, annulez le délai d'attente s'il y a une valeur dans les données.

Supprimez les données lors du rappel du fondu.

Il est en fait moins coûteux d'utiliser mouseenter / mouseleave car ils ne se déclenchent pas pour le menu lorsque les enfants survolent / survolent.

mothmonsterman
la source
7
@Arthur l'a fait ici, avez-vous encore besoin d'informations? stackoverflow.com/a/1670561/152640
mothmonsterman
270

Ce code illustre ce que happytime harry et moi essayons de dire. Lorsque la souris entre, une info-bulle apparaît, lorsque la souris la quitte, elle fixe un délai pour qu'elle disparaisse. Si la souris entre dans le même élément avant le déclenchement du délai, nous détruisons le déclencheur avant qu'il ne s'éteigne en utilisant les données que nous avons stockées auparavant.

$("someelement").mouseenter(function(){
    clearTimeout($(this).data('timeoutId'));
    $(this).find(".tooltip").fadeIn("slow");
}).mouseleave(function(){
    var someElement = $(this),
        timeoutId = setTimeout(function(){
            someElement.find(".tooltip").fadeOut("slow");
        }, 650);
    //set the timeoutId, allowing us to clear this trigger if the mouse comes back over
    someElement.data('timeoutId', timeoutId); 
});
Arthur Goldsmith
la source
126

AVERTISSEMENT: is(':hover')est déconseillé dans jquery 1.8+. Voir cet article pour une solution.

Vous pouvez également utiliser cette réponse: https://stackoverflow.com/a/6035278/8843 pour tester si la souris survole un élément:

$('#test').click(function() {
    if ($('#hello').is(':hover')) {
        alert('hello');
    }
});
tal
la source
5
Cela n'est documenté nulle part (afik) et ne semble pas être précis avec des éléments affichés dynamiquement (comme un menu) ..
lambinator
12
cassé à partir de jQuery 1.9.1 !! utilisez plutôt la solution d'Ivo
mathheadinclouds
1
Erreur non interceptée: erreur de syntaxe, expression non reconnue: pseudo non pris en charge: survol
Julio Marins
1
Avertissement : :hovern'est pas un sélecteur jQuery valide: api.jquery.com/category/selectors (source: bugs.jquery.com/ticket/11574 )
Pang
1
Cela fonctionne toujours si le navigateur prend en chargedocument.querySelectorAll(':hover')
ekuusela
34

Vous pouvez utiliser l' hoverévénement de jQuery pour effectuer le suivi manuellement:

$(...).hover(
    function() { $.data(this, 'hover', true); },
    function() { $.data(this, 'hover', false); }
).data('hover', false);

if ($(something).data('hover'))
    //Hovered!
SLaks
la source
1
pourquoi utiliser data () et pas ajouter / removeclass ()? L'un est-il plus performant que l'autre?
psychotik
2
@psychotik: Oui; $.datan'implique pas de manipulation de chaîne.
SLaks
J'ai enveloppé cela dans une classe: stackoverflow.com/questions/1273566/…
ripper234
24

J'avais besoin de quelque chose exactement comme ça (dans un environnement un peu plus complexe et la solution avec beaucoup de `` mouseenters '' et `` mouseleaves '' ne fonctionnait pas correctement), j'ai donc créé un petit plugin jquery qui ajoute la méthode ismouseover. Jusqu'à présent, cela a plutôt bien fonctionné.

//jQuery ismouseover  method
(function($){ 
    $.mlp = {x:0,y:0}; // Mouse Last Position
    function documentHandler(){
        var $current = this === document ? $(this) : $(this).contents();
        $current.mousemove(function(e){jQuery.mlp = {x:e.pageX,y:e.pageY}});
        $current.find("iframe").load(documentHandler);
    }
    $(documentHandler);
    $.fn.ismouseover = function(overThis) {  
        var result = false;
        this.eq(0).each(function() {  
                var $current = $(this).is("iframe") ? $(this).contents().find("body") : $(this);
                var offset = $current.offset();             
                result =    offset.left<=$.mlp.x && offset.left + $current.outerWidth() > $.mlp.x &&
                            offset.top<=$.mlp.y && offset.top + $current.outerHeight() > $.mlp.y;
        });  
        return result;
    };  
})(jQuery);

Ensuite, à n'importe quel endroit du document, appelez-le comme ceci et il renvoie vrai ou faux:

$("#player").ismouseover()

Je l'ai testé sur IE7 +, Chrome 1+ et Firefox 4 et fonctionne correctement.

Ivan Castellanos
la source
Il ne semble pas fonctionner sur mouseenter (Chrome) - codepen.io/anon/pen/kcypB
wrygiel
Parfait. Expression de fonction immédiatement invoquée (IIFE) qui résout le problème du ciblage des objets sous l'élément avec une superposition d'opacité. Brillant! Merci pour ça.
Alexander Dixon
10

Dans jQuery, vous pouvez utiliser .is (': hover'), donc

function IsMouseOver(oi)
{
   return $(oi).is(':hover');
}

serait désormais le moyen le plus concis de fournir la fonction demandée dans le PO.

Remarque: ce qui précède ne fonctionne pas dans IE8 ou inférieur

Comme alternative moins succincte qui fonctionne dans IE8 (si je peux faire confiance au mode IE8 d'IE9), et le fait sans déclencher $(...).hover(...)partout, ni ne nécessite de connaître un sélecteur pour l'élément (dans ce cas, la réponse d'Ivo est plus facile):

function IsMouseOver(oi)
{
    return oi.length && 
           oi.parent()
             .find(':hover')
             .filter(function(s){return oi[0]==this})
             .length > 0;
}
towr
la source
Ce n'est pas un sélecteur jQuery valide! Les gens doivent cesser de suggérer cette méthode. Il est partout et n'est pas compatible avec IE8.
Sanne
Voir mon autre réponse pour avoir une solution pour IE8
Sanne
2
@Sanne Il est curieux, parce que $(':hover') fait le travail dans IE8. C'est un pseudo sélecteur CSS2 valide, donc il devrait fonctionner.
TOWR
7

J'ai pris l'idée de SLaks et l'ai emballée dans une petite classe .

function HoverWatcher(selector){
  this.hovering = false;
  var self = this; 

  this.isHoveringOver = function() { 
    return self.hovering; 
  } 

    $(selector).hover(function() { 
      self.hovering = true; 
    }, function() { 
      self.hovering = false; 
    }) 
} 

var box1Watcher = new HoverWatcher('#box1');
var box2Watcher = new HoverWatcher('#box2');



$('#container').click(function() {
  alert("box1.hover = " + box1Watcher.isHoveringOver() +
        ", box2.hover = " + box2Watcher.isHoveringOver());
});
ripper234
la source
6

JUSTE FYI pour les futurs trouveurs de cela.

J'ai créé un plugin jQuery qui peut faire cela et bien plus encore. Dans mon plugin, pour obtenir tous les éléments sur lesquels le curseur est actuellement survolé, procédez simplement comme suit:

$.cursor("isHover"); // will return jQ object of all elements the cursor is 
                     // currently over & doesn't require timer

Comme je l'ai mentionné, il a également beaucoup d'autres utilisations comme vous pouvez le voir dans le

jsFiddle trouvé ici

SpYk3HH
la source
5

Comme je ne peux pas commenter, je vais donc écrire ceci comme réponse!

Veuillez comprendre la différence entre le sélecteur css ": hover" et l'événement hover!

": hover" est un sélecteur css et a en effet été supprimé avec l'événement lorsqu'il était utilisé comme ceci $("#elementId").is(":hover") , mais dans ce sens, cela n'a vraiment rien à voir avec le survol de l'événement jQuery.

si vous codez $("#elementId:hover") , l'élément ne sera sélectionné que lorsque vous survolerez avec la souris. la déclaration ci-dessus fonctionnera avec toutes les versions de jQuery en sélectionnant cet élément avec une sélection CSS pure et légitime.

D'un autre côté, le survol de l'événement qui est

$("#elementId").hover(
     function() { 
         doSomething(); 
     }
); 

est en effet déconseillé comme jQuery 1.8 ici l'état du site jQuery:

Lorsque le nom d'événement "hover" est utilisé, le sous-système d'événement le convertit en "mouseenter mouseleave" dans la chaîne d'événement. C'est ennuyeux pour plusieurs raisons:

Sémantique: le vol stationnaire n'est pas la même chose que l'entrée et la sortie de la souris d'un élément, cela implique une certaine décélération ou retard avant le tir. Nom de l'événement: le event.type renvoyé par le gestionnaire attaché n'est pas en survol, mais soit mouseenter ou mouseleave. Aucun autre événement ne fait cela. Cooptation du nom "hover": Il n'est pas possible d'attacher un événement avec le nom "hover" et de le déclencher en utilisant .trigger ("hover"). Les docs appellent déjà ce nom "fortement déconseillé pour le nouveau code", je voudrais le déprécier officiellement pour 1.8 et finalement le supprimer.

La raison pour laquelle ils ont supprimé l'utilisation (": hover") n'est pas claire, mais bon, vous pouvez toujours l'utiliser comme ci-dessus et voici un petit hack pour continuer à l'utiliser.

(function ($) {
   /** 
    * :hover selector was removed from jQuery 1.8+ and cannot be used with .is(":hover") 
    * but using it in this way it works as :hover is css selector! 
    *
    **/
    $.fn.isMouseOver = function() {
        return $(this).parent().find($(this).selector + ":hover").length > 0;
    };
})(jQuery);

Oh et je ne recommanderais pas la version timeout car cela apporte beaucoup de complexité , utilisez des fonctionnalités de timeout pour ce genre de choses s'il n'y a pas d'autre moyen et croyez-moi, dans 95% des cas, il y a une autre façon !

J'espère pouvoir aider quelques personnes là-bas.

Greetz Andy

Phenix
la source
2

Merci à vous deux. À un moment donné, j'ai dû renoncer à essayer de détecter si la souris était encore au-dessus de l'élément. Je sais que c'est possible, mais peut nécessiter trop de code pour accomplir.

Cela m'a pris un peu de temps, mais j'ai pris vos deux suggestions et j'ai trouvé quelque chose qui pourrait fonctionner pour moi.

Voici un exemple simplifié (mais fonctionnel):

$("[HoverHelp]").hover (
    function () {
        var HelpID = "#" + $(this).attr("HoverHelp");
        $(HelpID).css("top", $(this).position().top + 25);
        $(HelpID).css("left", $(this).position().left);
        $(HelpID).attr("fadeout", "false");
        $(HelpID).fadeIn();
    },
    function () {
        var HelpID = "#" + $(this).attr("HoverHelp");
        $(HelpID).attr("fadeout", "true");
        setTimeout(function() { if ($(HelpID).attr("fadeout") == "true") $(HelpID).fadeOut(); }, 100);
    }
);

Et puis, pour faire fonctionner ce texte, voici tout ce que j'ai à faire:

<div id="tip_TextHelp" style="display: none;">This help text will show up on a mouseover, and fade away 100 milliseconds after a mouseout.</div>

This is a <span class="Help" HoverHelp="tip_TextHelp">mouse over</span> effect.

Avec beaucoup de CSS fantaisistes, cela permet des info-bulles d'aide très intéressantes. Soit dit en passant, j'avais besoin du retard dans la sortie de la souris en raison de minuscules écarts entre les cases à cocher et le texte qui faisaient clignoter l'aide lorsque vous déplacez la souris. Mais cela fonctionne comme un charme. J'ai également fait quelque chose de similaire pour les événements de focus / flou.

Steve Wortham
la source
2

Je vois beaucoup de délais d'attente pour cela, mais dans le contexte d'un événement, ne pouvez-vous pas regarder les coordonnées, comme ceci?:

function areXYInside(e){  
        var w=e.target.offsetWidth;
        var h=e.target.offsetHeight;
        var x=e.offsetX;
        var y=e.offsetY;
        return !(x<0 || x>=w || y<0 || y>=h);
}

Selon le contexte, vous devrez peut-être vous assurer (this == e.target) avant d'appeler areXYInside (e).

fyi- Je cherche à utiliser cette approche dans un gestionnaire dragLeave, afin de confirmer que l'événement dragLeave n'a pas été déclenché en entrant dans un élément enfant. Si vous ne vérifiez pas que vous êtes toujours à l'intérieur de l'élément parent, vous pouvez par erreur prendre des mesures destinées uniquement lorsque vous quittez vraiment le parent.

EDIT: c'est une bonne idée, mais ne fonctionne pas assez régulièrement. Peut-être avec quelques petits ajustements.

dlo
la source
2

Vous pouvez tester avec jQuerysi un enfant div a une certaine classe. Ensuite, en appliquant cette classe lorsque vous passez la souris sur et en dehors d'un certain div, vous pouvez tester si votre souris est dessus, même lorsque vous passez la souris sur un élément différent sur la page Beaucoup moins de code de cette façon. J'ai utilisé cela parce que j'avais des espaces entre les divs dans une fenêtre pop-up, et je ne voulais fermer la fenêtre pop-up que lorsque je quittais la fenêtre pop-up, pas lorsque je passais ma souris sur les espaces dans la fenêtre pop-up. J'ai donc appelé une fonction de survol sur la div de contenu (sur laquelle la fenêtre contextuelle était terminée), mais cela ne déclencherait la fonction de fermeture que lorsque je survolais la div de contenu, ET était en dehors de la fenêtre contextuelle!

$ (". pop-up"). survol (fonction (e)
    {
    $ (this) .addClass ("over");
    });

$ (". pop-up"). mouseout (fonction (e)
    {
    $ (this) .removeClass ("over");
    });


$ ("# mainContent"). mouseover (fonction (e) {
            if (! $ (". expand"). hasClass ("over")) {
            Drupal.dhtmlMenu.toggleMenu ($ (". Expanded"));
        }
    });

Nate
la source
2

Ce serait la façon la plus simple de le faire!

  function(oi) 
  {
   if(!$(oi).is(':hover')){$(oi).fadeOut(100);}
  }
Kareem
la source
2

Voici une technique qui ne repose pas sur jquery et utilise l' matches API DOM native . Il utilise des préfixes de fournisseurs pour prendre en charge les navigateurs remontant à IE9. Voir matchesselector sur caniuse.com pour plus de détails.

Créez d'abord la fonction matchesSelector, comme ceci:

var matchesSelector = (function(ElementPrototype) {
var fn = ElementPrototype.matches ||
          ElementPrototype.webkitMatchesSelector ||
          ElementPrototype.mozMatchesSelector ||
          ElementPrototype.msMatchesSelector;

return function(element, selector) {
  return fn.call(element, selector);
};

})(Element.prototype);

Ensuite, pour détecter le vol stationnaire:

var mouseIsOver = matchesSelector(element, ':hover');
Andrew Newdigate
la source
1

J'ai répondu à cette question dans une autre question, avec tous les détails dont vous pourriez avoir besoin:

Détecter SI survolant l'élément avec jQuery (a 99 votes positifs au moment de la rédaction)

Fondamentalement, vous pouvez faire quelque chose comme:

var ishovered = oi.is(":hover");

Cela ne fonctionne que si oiest un objet jQuery contenant un seul élément. S'il y a plusieurs éléments correspondants, vous devez appliquer à chaque élément, par exemple:

var hoveredItem = !!$('ol>li').filter(function() { return $(this).is(":hover"); });
                  // not .filter(':hover'), as we can't apply :hover on multiple elements

Cela a été testé à partir de jQuery 1.7.

Meligy
la source
1

Voici une fonction qui vous aide à vérifier si la souris est à l'intérieur d'un élément ou non. La seule chose que vous devez faire est d'appeler la fonction où vous pouvez avoir un EventObject associé à la souris. quelque chose comme ça:

$("body").mousemove(function(event){
     element_mouse_is_inside($("#mycontainer", event, true, {});
});

Vous pouvez voir le code source ici dans github ou en bas de l'article:

https://github.com/mostafatalebi/ElementsLocator/blob/master/elements_locator.jquery.js

function element_mouse_is_inside  (elementToBeChecked, mouseEvent, with_margin, offset_object)
{
    if(!with_margin)
    {
        with_margin = false;
    }
    if(typeof offset_object !== 'object')
    {
        offset_object = {};
    }
    var elm_offset = elementToBeChecked.offset();
    var element_width = elementToBeChecked.width();
    element_width += parseInt(elementToBeChecked.css("padding-left").replace("px", ""));
    element_width += parseInt(elementToBeChecked.css("padding-right").replace("px", ""));
    var element_height = elementToBeChecked.height();
    element_height += parseInt(elementToBeChecked.css("padding-top").replace("px", ""));
    element_height += parseInt(elementToBeChecked.css("padding-bottom").replace("px", ""));
    if( with_margin)
    {
        element_width += parseInt(elementToBeChecked.css("margin-left").replace("px", ""));
        element_width += parseInt(elementToBeChecked.css("margin-right").replace("px", ""));
        element_height += parseInt(elementToBeChecked.css("margin-top").replace("px", ""));
        element_height += parseInt(elementToBeChecked.css("margin-bottom").replace("px", ""));
    }

    elm_offset.rightBorder = elm_offset.left+element_width;
    elm_offset.bottomBorder = elm_offset.top+element_height;

    if(offset_object.hasOwnProperty("top"))
    {
        elm_offset.top += parseInt(offset_object.top);
    }
    if(offset_object.hasOwnProperty("left"))
    {
        elm_offset.left += parseInt(offset_object.left);
    }
    if(offset_object.hasOwnProperty("bottom"))
    {
        elm_offset.bottomBorder += parseInt(offset_object.bottom);
    }
    if(offset_object.hasOwnProperty("right"))
    {
        elm_offset.rightBorder += parseInt(offset_object.right);
    }
    var mouseX = mouseEvent.pageX;
    var mouseY = mouseEvent.pageY;

    if(  (mouseX > elm_offset.left && mouseX < elm_offset.rightBorder)
        && (mouseY > elm_offset.top && mouseY < elm_offset.bottomBorder) )
    {
        return true;
    }
    else
    {
        return false;
    }
}
Mostafa Talebi
la source
0

S'étendant sur ce que `` Happytime harry '' a dit, assurez-vous d'utiliser la fonction jquery .data () pour stocker l'ID de délai d'attente. Cela vous permet de récupérer très facilement l'ID de délai d'expiration lorsque le «mouseenter» est déclenché ultérieurement sur ce même élément, ce qui vous permet d'éliminer le déclencheur pour que votre info-bulle disparaisse.

Arthur Goldsmith
la source
0

Vous pouvez utiliser les événements mouseenter et mouseleave de jQuery. Vous pouvez définir un indicateur lorsque la souris entre dans la zone souhaitée et désactiver le drapeau lorsqu'il quitte la zone.

mikerobi
la source
1
Voilà ce que je pensais faire. Utiliser $ .data () comme le suggère SLaks semble être un bon moyen d'y parvenir.
JamesBrownIsDead
0

J'ai combiné les idées de ce sujet et j'ai trouvé ceci, qui est utile pour afficher / masquer un sous-menu:

$("#menu_item_a").mouseenter(function(){
   clearTimeout($(this).data('timeoutId'));
   $("#submenu_a").fadeIn("fast");
}).mouseleave(function(){
   var menu_item = $(this);

   var timeoutId = setTimeout(function(){
      if($('#submenu_a').is(':hover'))
      {
        clearTimeout(menu_item.data('timeoutId'));
      }
      else
      {
        $("#submenu_a").fadeOut("fast");
      }
   }, 650);

    menu_item.data('timeoutId', timeoutId); 
});

 $("#submenu_a").mouseleave(function(){
   $(this).fadeOut("fast");
 });

Semble fonctionner pour moi. J'espère que cela aide quelqu'un.

EDIT: Réaliser maintenant que cette approche ne fonctionne pas correctement dans IE.

Deux piliers
la source
0

Je n'ai pu utiliser aucune des suggestions ci-dessus.
Pourquoi je préfère ma solution?
Cette méthode vérifie si la souris survole un élément à tout moment choisi par vous .
Mouseenter et : hover sont cool, mais mouseenter ne se déclenche que si vous déplacez la souris, pas lorsque l'élément se déplace sous la souris.
: le vol stationnaire est assez doux mais ... IE

Alors je fais ça:

Non 1. stockez la position x, y de la souris à chaque fois qu'elle est déplacée quand vous en avez besoin,
Non 2. vérifiez si la souris survole l'un des éléments qui correspondent à la requête faites des trucs ... comme déclencher un événement mouseenter

// define mouse x, y variables so they are traced all the time
var mx = 0; //  mouse X position
var my = 0; //  mouse Y position

// update mouse x, y coordinates every time user moves the mouse
$(document).mousemove(function(e){
    mx = e.pageX;
    my = e.pageY;
});

// check is mouse is over an element at any time You need (wrap it in function if You need to)
$("#my_element").each(function(){
    boxX = $(this).offset().left;
    boxY = $(this).offset().top;
    boxW = $(this).innerWidth();
    boxH = $(this).innerHeight();
    if ((boxX <= mx) &&
        (boxX + 1000 >= mx) &&
        (boxY <= my) &&
        (boxY + boxH >= my))
    {
        // mouse is over it so you can for example trigger a mouseenter event
        $(this).trigger("mouseenter");
    }
});
Hyper Motion
la source
0

Juste une note sur la réponse populaire et utile d'Arthur Goldsmith ci-dessus: si vous déplacez votre souris d'un élément à un autre dans IE (au moins jusqu'à IE 9), vous pouvez avoir du mal à le faire fonctionner correctement si le nouvel élément a un fond transparent (ce qu'il ferait par défaut). Ma solution de contournement consistait à donner au nouvel élément une image d'arrière-plan transparente.

Jish
la source
0
$(document).hover(function(e) {
    alert(e.type === 'mouseenter' ? 'enter' : 'leave');
});

VIOLON

yckart
la source
-1

Vous pouvez utiliser is(':visible'); dans jquery Et pour $ ('. Item: hover') cela fonctionne aussi dans Jquery.

ceci est un snnipet de code htm:

    <li class="item-109 deeper parent">
<a class="root" href="/Comsopolis/index.php/matiers"><span>Matiers</span></a>
<ul>
<li class="item-110 noAff">
<a class=" item sousMenu" href="/Comsopolis/index.php/matiers/tsdi">
<span>Tsdi</span>
</a>
</li>
<li class="item-111 noAff">
<a class="item" href="/Comsopolis/index.php/matiers/reseaux">
<span>Réseaux</span>
</a>
</li>
</ul>
</li>

et voici le code JS:

$('.menutop > li').hover(function() {//,.menutop li ul

    $(this).find('ul').show('fast');

},function() {
    if($(this).find('ul').is(':hover'))
    $(this).hide('fast');

});

 $('.root + ul').mouseleave(function() {
    if($(this).is(':visible'))
    $(this).hide('fast');

});

c'est ce dont je parlais :)

ucefkh
la source
1
Je ne vois pas en quoi cela est lié à la question posée.
Andrew Barber
vous pouvez l'utiliser lorsque vous sortez de la souris et affichez votre élément caché et après un délai, vous pouvez vérifier s'il est visible lorsque la souris entre dans l'élément cible que vous souhaitez masquer / afficher
ucefkh
1
Je ne pense pas que vous ayez bien lu la question. Ce n'est pas du tout ce dont il a besoin.
Andrew Barber
1
Ce que vous faites n'est pas lié à cette question . (apparemment (
Andrew Barber