ChatGPT解决这个技术问题 Extra ChatGPT

How do I check if the mouse is over an element in jQuery?

Is there a quick & easy way to do this in jQuery that I'm missing?

I don't want to use the mouseover event because I'm already using it for something else. I just need to know if the mouse is over an element at a given moment.

I'd like to do something like this, if only there was an "IsMouseOver" function:

function hideTip(oi) {
    setTimeout(function() { if (!IsMouseOver(oi)) $(oi).fadeOut(); }, 100);
}
For most purposes the answers given are sufficient, but there are cases when mousein/out isn't sufficient. For example, hiding a menu when the mouse is no longer over either the menu head OR the menu body.
I have used the method described in my answer to work with icons (mouse events for button borders) that open animated, delayed closing dropdowns. You handle the delay/cancel delay in both the icon and the dropdown by using jquery's triggerHandler methods. Fully sufficient.
#Marcus: If hiding a menu, what is the better way to go about it?
I would have voted this up if the best answer had been marked as the solution.

I
Ivo

A clean and elegant hover check:

if ($('#element:hover').length != 0) {
    // do something ;)
}

exactly - this should be the selected answer! Anyway, here is a fiddle for it jsfiddle.net/mathheadinclouds/ZKGqe
How to check if an element is hovered: stackoverflow.com/questions/8981463/…
C
Community

This code illustrates what happytime harry and I are trying to say. When the mouse enters, a tooltip comes out, when the mouse leaves it sets a delay for it to disappear. If the mouse enters the same element before the delay is triggered, then we destroy the trigger before it goes off using the data we stored before.

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

C
Community

WARNING: is(':hover') is deprecated in jquery 1.8+. See this post for a solution.

You can also use this answer : https://stackoverflow.com/a/6035278/8843 to test if the mouse is hover an element :

$('#test').click(function() {
    if ($('#hello').is(':hover')) {
        alert('hello');
    }
});

This isn't documented anywhere (afik) and doesn't seem to be accurate with dynamically shown elements (like a menu)..
broken as of jQuery 1.9.1!! use Ivo's solution instead
Uncaught Error: Syntax error, unrecognized expression: unsupported pseudo: hover
Warning: :hover is not a valid jQuery selector: api.jquery.com/category/selectors (source: bugs.jquery.com/ticket/11574)
It still works if the browser supports document.querySelectorAll(':hover')
m
mothmonsterman

Set a timeout on the mouseout to fadeout and store the return value to data in the object. Then onmouseover, cancel the timeout if there is a value in the data.

Remove the data on callback of the fadeout.

It is actually less expensive to use mouseenter/mouseleave because they do not fire for the menu when children mouseover/mouseout fire.


@Arthur did right here, do you still need more info? stackoverflow.com/a/1670561/152640
S
SLaks

You can use jQuery's hover event to keep track manually:

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

if ($(something).data('hover'))
    //Hovered!

why use data() and not add/removeclass()? Is one more performant than the other?
@psychotik: Yes; $.data doesn't involve string manipulation.
I wrapped this in a class: stackoverflow.com/questions/1273566/…
I
Ivan Castellanos

I needed something exactly as this (in a little more complex environment and the solution with a lot of 'mouseenters' and 'mouseleaves' wasnt working properly) so i created a little jquery plugin that adds the method ismouseover. It has worked pretty well so far.

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

Then in any place of the document yo call it like this and it returns true or false:

$("#player").ismouseover()

I tested it on IE7+, Chrome 1+ and Firefox 4 and is working properly.


It doesn't seem to work on mouseenter (Chrome) - codepen.io/anon/pen/kcypB
Perfect. Immediately invoked function expression (IIFE) that solves the issue of targeting objects underneath element with opacity overlay. Brilliant! Thank you for this.
t
towr

In jQuery you can use .is(':hover'), so

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

would now be the most concise way to provide the function requested in the OP.

Note: The above does not work in IE8 or lower

As less succinct alternative that does work in IE8 (if I can trust IE9's IE8 modus), and does so without triggering $(...).hover(...) all over the place, nor requires knowing a selector for the element (in which case Ivo's answer is easier):

function IsMouseOver(oi)
{
    return oi.length && 
           oi.parent()
             .find(':hover')
             .filter(function(s){return oi[0]==this})
             .length > 0;
}

This is not valid jQuery selector! People have to stop suggesting this method. It's everywhere and not compatible with IE8.
See my other answer to have a solution for IE8
@Sanne It's curious, because $(':hover') does work in IE8. It's a valid CSS2 pseudo selector, so it ought to work.
r
ripper234

I took SLaks' idea and wrapped it in a small class.

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

S
SpYk3HH

JUST FYI for future finders of this.

I made a jQuery plugin that can do this and a lot more. In my plugin, to get all elements the cursor is currently hovered over, simply do the following:

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

As I mentioned, it also has alot of other uses as you can see in the

jsFiddle found here


P
Phenix

As I cannot comment, so I will write this as an answer!

Please understand the difference between css selector ":hover" and the hover event!

":hover" is a css selector and was indeed removed with the event when used like this $("#elementId").is(":hover"), but in it's meaning it has really nothing to do with the jQuery event hover.

if you code $("#elementId:hover"), the element will only be selected when you hover with the mouse. the above statement will work with all jQuery versions as your selecting this element with pure and legit css selection.

On the other hand the event hover which is

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

is indeed deprecaded as jQuery 1.8 here the state from jQuery website:

When the event name "hover" is used, the event subsystem converts it to "mouseenter mouseleave" in the event string. This is annoying for several reasons: Semantics: Hovering is not the same as the mouse entering and leaving an element, it implies some amount of deceleration or delay before firing. Event name: The event.type returned by the attached handler is not hover, but either mouseenter or mouseleave. No other event does this. Co-opting the "hover" name: It is not possible to attach an event with the name "hover" and fire it using .trigger("hover"). The docs already call this name "strongly discouraged for new code", I'd like to deprecate it officially for 1.8 and eventually remove it.

Why they removed the usage is(":hover") is unclear but oh well, you can still use it like above and here is a little hack to still use it.

(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 and I would not recomment the timeout version as this brings a lot of complexity, use timeout functionalities for this kind of stuff if there is no other way and believe me, in 95% percent of all cases there is another way!

Hope I could help a couple people out there.

Greetz Andy


S
Steve Wortham

Thanks to both of you. At some point I had to give up on trying to detect if the mouse was still over the element. I know it's possible, but may require too much code to accomplish.

It took me a little while but I took both of your suggestions and came up with something that would work for me.

Here's a simplified (but functional) example:

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

And then to make this work on some text this is all I have to do:

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

Along with a lot of fancy CSS, this allows some very nice mouseover help tooltips. By the way, I needed the delay in the mouseout because of tiny gaps between checkboxes and text that was causing the help to flash as you move the mouse across. But this works like a charm. I also did something similar for the focus/blur events.


d
dlo

I see timeouts used for this a lot, but in the context of an event, can't you look at coordinates, like this?:

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

Depending on context, you may need to make sure (this==e.target) before calling areXYInside(e).

fyi- I'm looking at using this approach inside a dragLeave handler, in order to confirm that the dragLeave event wasn't triggered by going into a child element. If you don't somehow check that you're still inside the parent element, you might mistakenly take action that's meant only for when you truly leave the parent.

EDIT: this is a nice idea, but does not work consistently enough. Perhaps with some small tweaks.


T
Taryn

You can test with jQuery if any child div has a certain class. Then by applying that class when you mouse over and out out a certain div, you can test whether your mouse is over it, even when you mouse over a different element on the page Much less code this way. I used this because I had spaces between divs in a pop-up, and I only wanted to close the pop up when I moved off of the pop up, not when I was moving my mouse over the spaces in the pop up. So I called a mouseover function on the content div (which the pop up was over), but it would only trigger the close function when I moused-over the content div, AND was outside the pop up!

$(".pop-up").mouseover(function(e)
    {
    $(this).addClass("over");
    });

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


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

K
Kareem

This would be the easiest way of doing it!

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

A
Andrew Newdigate

Here's a technique which doesn't rely on jquery and uses the native DOM matches API. It uses vendor prefixes to support browsers going back to IE9. See matchesselector on caniuse.com for full details.

First create the matchesSelector function, like so:

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

Then, to detect hover:

var mouseIsOver = matchesSelector(element, ':hover');

C
Community

I have answered this in another question, with all details you may need:

Detect IF hovering over element with jQuery (has 99 upvotes at the time of writing)

Basically, you can do something like:

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

This works only if oi is a jQuery object containing a single element. If there are multiple elements matched, you need to apply to each element, for example:

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

This was tested starting jQuery 1.7.


M
Mostafa Talebi

Here is a function which helps you check if the mouse is inside an element or not. The only thing you should do is to call the function where you can have a live mouse-associated EventObject. something like this:

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

You can see the source code here in github or at the bottom of the post:

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

A
Arthur Goldsmith

Extending on what 'Happytime harry' said, be sure to use the .data() jquery function to store the timeout id. This is so that you can retrieve the timeout id very easily when the 'mouseenter' is triggered on that same element later, allowing you to eliminate the trigger for your tooltip to disappear.


m
mikerobi

You can use jQuery's mouseenter and mouseleave events. You can set a flag when the mouse enters the desired area and unset the flag when it leaves the area.


That's what I was thinking of doing. Using $.data() as SLaks suggests seems to be a good way to accomplish this.
T
Two Piers

I combined ideas from this topic and came up with this, which is useful for showing/hiding a submenu:

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

Seems to work for me. Hope this helps someone.

EDIT: Now realizing this approach is not working correctly in IE.


H
Hyper Motion

I couldn't use any of the suggestions above. Why I prefer my solution? This method checks if mouse is over an element at any time chosen by You. Mouseenter and :hover are cool, but mouseenter triggers only if you move the mouse, not when element moves under the mouse. :hover is pretty sweet but ... IE

So I do this:

No 1. store mouse x, y position every time it's moved when you need to, No 2. check if mouse is over any of elements that match the query do stuff ... like trigger a mouseenter event

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

J
Jish

Just a note about the popular and helpful Arthur Goldsmith answer above: If you're moving your mouse from one element to another in IE (at least until IE 9) you may have some trouble getting this to work correctly if the new element has a transparent background (which it would by default). My workaround was the give the new element a transparent background image.


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

FIDDLE


u
ucefkh

You can use is(':visible'); in jquery And for $('.item:hover') it is working in Jquery also.

this is a htm code 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>

and this is the JS Code :

$('.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');

});

this what i was talking about :)


I'm not seeing how that's related to the question asked.
you can use this when you go out from mouseover and display your hidden element and after a delay you can check if it is visible when the mouse enter the target element you want to hide/show
I don't think you read the question very well at all. This isn't at all what he needs.
What you are doing isn't related to this question. (apparently(