¿Cómo verifico si el mouse está sobre un elemento en jQuery?

265

¿Hay una manera rápida y fácil de hacer esto en jQuery que me falta?

No quiero usar el evento mouseover porque ya lo estoy usando para otra cosa. Solo necesito saber si el mouse está sobre un elemento en un momento dado.

Me gustaría hacer algo como esto, si solo hubiera una función "IsMouseOver":

function hideTip(oi) {
    setTimeout(function() { if (!IsMouseOver(oi)) $(oi).fadeOut(); }, 100);
}
Steve Wortham
fuente
55
Para la mayoría de los propósitos, las respuestas dadas son suficientes, pero hay casos en que mousein / out no es suficiente. Por ejemplo, ocultar un menú cuando el mouse ya no está sobre el encabezado del menú O el cuerpo del menú.
Marcus Downing
He utilizado el método descrito en mi respuesta para trabajar con íconos (eventos del mouse para bordes de botones) que abren menús desplegables animados de cierre retrasado. Maneja la demora de retraso / cancelación tanto en el icono como en el menú desplegable utilizando los métodos triggerHandler de jquery. Completamente suficiente
mothmonsterman
#Marcus: Si escondes un menú, ¿cuál es la mejor manera de hacerlo?
coderama
Hubiera votado esto si la mejor respuesta hubiera sido marcada como la solución.
BBaysinger el

Respuestas:

97

Establezca un tiempo de espera en el mouseout para desvanecerse y almacenar el valor de retorno a los datos en el objeto. Luego, sobre el mouse, cancele el tiempo de espera si hay un valor en los datos.

Elimine los datos en la devolución de llamada del desvanecimiento.

En realidad, es menos costoso usar mouseenter / mouseleave porque no se activan para el menú cuando se activa el mouseover / mouseout de los niños.

mothmonsterman
fuente
77
@Arthur lo hizo aquí, ¿todavía necesitas más información? stackoverflow.com/a/1670561/152640
mothmonsterman
270

Este código ilustra lo que Happytime Harry y yo estamos tratando de decir. Cuando el mouse entra, sale una información sobre herramientas, cuando el mouse se va establece un retraso para que desaparezca. Si el mouse ingresa al mismo elemento antes de que se active el retraso, entonces destruimos el activador antes de que se active utilizando los datos que almacenamos antes.

$("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
fuente
126

ADVERTENCIA: is(':hover')está en desuso en jquery 1.8+. Ver esta publicación para una solución.

También puede usar esta respuesta: https://stackoverflow.com/a/6035278/8843 para probar si el mouse está sobre un elemento:

$('#test').click(function() {
    if ($('#hello').is(':hover')) {
        alert('hello');
    }
});
tal
fuente
55
Esto no está documentado en ningún lado (afik) y no parece ser preciso con elementos mostrados dinámicamente (como un menú) ..
lambinator
12
roto a partir de jQuery 1.9.1 !! use la solución de Ivo en su lugar
mathheadinclouds
1
Error no detectado: Error de sintaxis, expresión no reconocida: pseudo no soportado: hover
Julio Marins
1
Advertencia : :hoverno es un selector jQuery válido: api.jquery.com/category/selectors (fuente: bugs.jquery.com/ticket/11574 )
Pang
1
Todavía funciona si el navegador es compatibledocument.querySelectorAll(':hover')
ekuusela
34

Puede usar el hoverevento de jQuery para realizar un seguimiento de forma manual:

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

if ($(something).data('hover'))
    //Hovered!
SLaks
fuente
1
¿Por qué usar data () y no agregar / eliminar clase ()? ¿Es uno más eficaz que el otro?
psychotik
2
@psychotik: sí; $.datano involucra manipulación de cuerdas.
SLaks
Envolví esto en una clase: stackoverflow.com/questions/1273566/…
ripper234
24

Necesitaba algo exactamente como esto (en un entorno un poco más complejo y la solución con muchos 'mouseenters' y 'mouseleaves' no funcionaba correctamente), así que creé un pequeño complemento jquery que agrega el método ismouseover. Ha funcionado bastante bien hasta ahora.

//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);

Luego, en cualquier lugar del documento, lo llama así y devuelve verdadero o falso:

$("#player").ismouseover()

Lo probé en IE7 +, Chrome 1+ y Firefox 4 y funciona correctamente.

Ivan Castellanos
fuente
Parece que no funciona en mouseenter (Chrome) - codepen.io/anon/pen/kcypB
wrygiel
Perfecto. Expresión de función invocada inmediatamente (IIFE) que resuelve el problema de apuntar objetos debajo del elemento con superposición de opacidad. ¡Brillante! Gracias por esto.
Alexander Dixon
10

En jQuery puedes usar .is (': hover'), entonces

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

ahora sería la forma más concisa de proporcionar la función solicitada en el OP.

Nota: lo anterior no funciona en IE8 o inferior

Como alternativa menos sucinta que funciona en IE8 (si puedo confiar en el modo IE8 de IE9), y lo hace sin disparar por $(...).hover(...)todas partes, ni requiere conocer un selector para el elemento (en cuyo caso la respuesta de Ivo es más fácil):

function IsMouseOver(oi)
{
    return oi.length && 
           oi.parent()
             .find(':hover')
             .filter(function(s){return oi[0]==this})
             .length > 0;
}
Towr
fuente
¡Esto no es válido jQuery selector! La gente tiene que dejar de sugerir este método. Está en todas partes y no es compatible con IE8.
Sanne
Vea mi otra respuesta para tener una solución para IE8
Sanne
2
@Sanne Es curioso, porque $(':hover') hace el trabajo en IE8. Es un pseudo selector CSS2 válido, por lo que debería funcionar.
Towr
7

Tomé la idea de SLaks y la envolví en una clase pequeña .

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

Solo para su información para futuros buscadores de esto.

Hice un complemento jQuery que puede hacer esto y mucho más. En mi complemento, para obtener todos los elementos sobre los que se encuentra el cursor, simplemente haga lo siguiente:

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

Como mencioné, también tiene muchos otros usos, como puede ver en el

jsFiddle encontrado aquí

SpYk3HH
fuente
5

Como no puedo comentar, ¡escribiré esto como respuesta!

¡Comprenda la diferencia entre el selector css ": hover" y el evento hover!

": hover" es un selector de CSS y de hecho se eliminó con el evento cuando se usa así $("#elementId").is(":hover") , pero en este sentido no tiene nada que ver con el evento de desplazamiento jQuery.

si codificas $("#elementId:hover") , el elemento solo se seleccionará cuando pase el mouse por encima. la declaración anterior funcionará con todas las versiones de jQuery al seleccionar este elemento con una selección de CSS pura y legítima.

Por otro lado, el evento se cierne, que es

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

De hecho, se considera como jQuery 1.8 aquí el estado del sitio web de jQuery:

Cuando se usa el nombre del evento "hover", el subsistema del evento lo convierte en "mouseenter mouseleave" en la cadena del evento. Esto es molesto por varias razones:

Semántica: el desplazamiento no es lo mismo que el mouse que entra y sale de un elemento, implica una cierta desaceleración o retraso antes de disparar. Nombre del evento: El event.type devuelto por el controlador adjunto no se desplaza, sino mouseenter o mouseleave. Ningún otro evento hace esto. Cooptar el nombre "hover": no es posible adjuntar un evento con el nombre "hover" y dispararlo usando .trigger ("hover"). Los documentos ya llaman a este nombre "fuertemente desaconsejado para el nuevo código", me gustaría desaprobarlo oficialmente para 1.8 y eventualmente eliminarlo.

Por qué eliminaron el uso es (": hover") no está claro, pero bueno, todavía puede usarlo como se indica arriba y aquí hay un pequeño truco para seguir usándolo.

(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);

Ah, y no recomendaría la versión de tiempo de espera ya que esto trae mucha complejidad , use las funcionalidades de tiempo de espera para este tipo de cosas si no hay otra manera y créanme, ¡en el 95% de los casos hay otra manera !

Espero poder ayudar a un par de personas por ahí.

Greetz Andy

Fénix
fuente
2

Gracias a los dos. En algún momento tuve que renunciar a tratar de detectar si el mouse todavía estaba sobre el elemento. Sé que es posible, pero puede requerir demasiado código para lograrlo.

Me tomó un poco de tiempo, pero tomé sus dos sugerencias y se me ocurrió algo que funcionaría para mí.

Aquí hay un ejemplo simplificado (pero funcional):

$("[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);
    }
);

Y luego, para que esto funcione en algún texto, esto es todo lo que tengo que hacer:

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

Junto con una gran cantidad de CSS sofisticado, esto permite algunas sugerencias de herramientas de ayuda al pasar el mouse. Por cierto, necesitaba el retraso en el mouseout debido a pequeños espacios entre las casillas de verificación y el texto que causaban que la ayuda parpadeara al mover el mouse. Pero esto funciona como un encanto. También hice algo similar para los eventos de enfoque / desenfoque.

Steve Wortham
fuente
2

Veo que los tiempos de espera se usan mucho para esto, pero en el contexto de un evento, ¿no puedes mirar las coordenadas, así?

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);
}

Dependiendo del contexto, es posible que deba asegurarse (this == e.target) antes de llamar a areXYInside (e).

fyi- Estoy buscando usar este enfoque dentro de un controlador dragLeave, para confirmar que el evento dragLeave no se activó al entrar en un elemento secundario. Si de alguna manera no verificas que todavía estás dentro del elemento padre, puedes tomar una acción por error que está destinada solo para cuando realmente dejas al padre.

EDITAR: esta es una buena idea, pero no funciona lo suficientemente consistente. Quizás con algunos pequeños ajustes.

dlo
fuente
2

Puede probar jQuerysi algún div hijo tiene una clase determinada. Luego, al aplicar esa clase cuando pasa el mouse sobre y fuera de un determinado div, puede probar si su mouse está sobre él, incluso cuando pasa el mouse sobre un elemento diferente en la página Mucho menos código de esta manera. Usé esto porque tenía espacios entre divs en una ventana emergente, y solo quería cerrar la ventana emergente cuando me movía fuera de la ventana emergente, no cuando movía mi mouse sobre los espacios en la ventana emergente. Así que llamé a una función de mouseover en el div de contenido (que terminó la ventana emergente), pero solo activaría la función de cierre cuando pasaba el mouse sobre el div de contenido, ¡Y estaba fuera de la ventana emergente!

$ (". pop-up"). mouseover (función (e)
    {
    $ (this) .addClass ("sobre");
    });

$ (". pop-up"). mouseout (función (e)
    {
    $ (this) .removeClass ("sobre");
    });


$ ("# mainContent"). mouseover (function (e) {
            if (! $ (". expandido"). hasClass ("over")) {
            Drupal.dhtmlMenu.toggleMenu ($ (". Expandido"));
        }
    });

Nate
fuente
2

¡Esta sería la forma más fácil de hacerlo!

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

Aquí hay una técnica que no se basa en jquery y utiliza la matches API DOM nativa . Utiliza prefijos de proveedor para admitir navegadores que se remontan a IE9. Ver partidosselector en caniuse.com para más detalles.

Primero cree la función matchSelector, así:

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

Luego, para detectar el desplazamiento:

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

He respondido esto en otra pregunta, con todos los detalles que pueda necesitar:

Detecta IF al pasar por encima del elemento con jQuery (tiene 99 votos a favor al momento de escribir)

Básicamente, puedes hacer algo como:

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

Esto funciona solo si oies un objeto jQuery que contiene un solo elemento. Si hay varios elementos coincidentes, debe aplicar a cada elemento, por ejemplo:

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

Esto se probó a partir de jQuery 1.7.

Meligy
fuente
1

Aquí hay una función que le ayuda a verificar si el mouse está dentro de un elemento o no. Lo único que debe hacer es llamar a la función donde puede tener un EventObject asociado al mouse en vivo. algo como esto:

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

Puedes ver el código fuente aquí en github o al final de la publicación:

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

Extendiendo lo que dijo 'Happytime harry', asegúrese de usar la función .data () jquery para almacenar el id de tiempo de espera. Esto es para que pueda recuperar la identificación de tiempo de espera muy fácilmente cuando el 'mouseenter' se activa en ese mismo elemento más adelante, lo que le permite eliminar el activador para que desaparezca su información sobre herramientas.

Arthur Goldsmith
fuente
0

Puede usar los eventos mouseenter y mouseleave de jQuery. Puede establecer una bandera cuando el mouse ingresa al área deseada y desarmar la bandera cuando sale del área.

Mikerobi
fuente
1
Eso es lo que estaba pensando hacer. Usar $ .data () como sugiere SLaks parece ser una buena manera de lograr esto.
JamesBrownIsDead
0

Combiné ideas de este tema y se me ocurrió esto, que es útil para mostrar / ocultar un submenú:

$("#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");
 });

Parece funcionar para mi. Espero que esto ayude a alguien.

EDITAR: ahora me doy cuenta de que este enfoque no funciona correctamente en IE.

Dos muelles
fuente
0

No pude usar ninguna de las sugerencias anteriores.
¿Por qué prefiero mi solución?
Este método verifica si el mouse está sobre un elemento en cualquier momento elegido por usted .
Mouseenter y : hover son geniales, pero mouseenter se activa solo si mueve el mouse, no cuando el elemento se mueve debajo del mouse.
: hover es bastante dulce pero ... IE

Entonces hago esto:

No 1. almacene la posición x, y del mouse cada vez que se mueva cuando lo necesite,
No 2. verifique si el mouse está sobre alguno de los elementos que coinciden con la consulta, haga cosas ... como activar un evento 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
fuente
0

Solo una nota sobre la popular y útil respuesta de Arthur Goldsmith anterior: si mueve el mouse de un elemento a otro en IE (al menos hasta IE 9), puede tener algunos problemas para que funcione correctamente si el nuevo elemento tiene un Fondo transparente (que sería por defecto). Mi solución fue darle al nuevo elemento una imagen de fondo transparente.

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

VIOLÍN

yckart
fuente
-1

Puedes usar is(':visible'); en jquery Y por $ ('. Item: hover') también está funcionando en Jquery.

este es un código htm snnipet:

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

y este es el código 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');

});

esto es de lo que estaba hablando :)

ucefkh
fuente
1
No veo cómo se relaciona eso con la pregunta formulada.
Andrew Barber
puede usar esto cuando salga del mouseover y muestre su elemento oculto y, después de un retraso, puede verificar si está visible cuando el mouse ingresa al elemento objetivo que desea ocultar / mostrar
ucefkh
1
No creo que leas la pregunta muy bien. Esto no es en absoluto lo que necesita.
Andrew Barber
1
Lo que estás haciendo no está relacionado con esta pregunta . (aparentemente (
Andrew Barber