156

I'm not looking for an action to call when hovering, but instead a way to tell if an element is being hovered over currently. For instance:

$('#elem').mouseIsOver(); // returns true or false

Is there a jQuery method that accomplishes this?

James Skidmore
  • 49,340
  • 32
  • 108
  • 136
  • Just in case someone is looking for other solutions http://stackoverflow.com/questions/1273566/how-do-i-check-if-the-mouse-is-over-an-element-in-jquery – Jo E. Jan 02 '14 at 07:06
  • 1
    The marking as duplicate looks quite wrong. The question does not say what the intent is, for someone else to read the mind of the OP and decide it's a collision detection question and mark as duplicate. – Meligy Apr 27 '16 at 01:18

12 Answers12

320

Original (And Correct) Answer:

You can use is() and check for the selector :hover.

var isHovered = $('#elem').is(":hover"); // returns true or false

Example: http://jsfiddle.net/Meligy/2kyaJ/3/

(This only works when the selector matches ONE element max. See Edit 3 for more)

.

Edit 1 (June 29, 2013): (Applicable to jQuery 1.9.x only, as it works with 1.10+, see next Edit 2)

This answer was the best solution at the time the question was answered. This ':hover' selector was removed with the .hover() method removal in jQuery 1.9.x.

Interestingly a recent answer by "allicarn" shows it's possible to use :hover as CSS selector (vs. Sizzle) when you prefix it with a selector $($(this).selector + ":hover").length > 0, and it seems to work!

Also, hoverIntent plugin mentioned in a another answer looks very nice as well.

Edit 2 (September 21, 2013): .is(":hover") works

Based on another comment I have noticed that the original way I posted, .is(":hover"), actually still works in jQuery, so.

  1. It worked in jQuery 1.7.x.

  2. It stopped working in 1.9.1, when someone reported it to me, and we all thought it was related to jQuery removing the hover alias for event handling in that version.

  3. It worked again in jQuery 1.10.1 and 2.0.2 (maybe 2.0.x), which suggests that the failure in 1.9.x was a bug or so not an intentional behaviour as we thought in the previous point.

If you want to test this in a particular jQuery version, just open the JSFidlle example at the beginning of this answer, change to the desired jQuery version and click "Run". If the colour changes on hover, it works.

.

Edit 3 (March 9, 2014): It only works when the jQuery sequence contains a single element

As shown by @Wilmer in the comments, he has a fiddle which doesn't even work against jQuery versions I and others here tested it against. When I tried to find what's special about his case I noticed that he was trying to check multiple elements at a time. This was throwing Uncaught Error: Syntax error, unrecognized expression: unsupported pseudo: hover.

So, working with his fiddle, this does NOT work:

var isHovered = !!$('#up, #down').filter(":hover").length;

While this DOES work:

var isHovered = !!$('#up,#down').
                    filter(function() { return $(this).is(":hover"); }).length;

It also works with jQuery sequences that contain a single element, like if the original selector matched only one element, or if you called .first() on the results, etc.

This is also referenced at my JavaScript + Web Dev Tips & Resources Newsletter.

Meligy
  • 35,654
  • 11
  • 85
  • 109
  • 18
    Throws an error in IE 8: `Syntax Error, unrecognized expression: hover. jquery-1.7.js line 4179`. – RobG Jan 24 '12 at 05:06
  • 1
    That's true. Just tested it. For IE6, try this hack described here http://peterned.home.xs4all.nl/csshover.html or fall back to the normal hover add some state and clear it later kind of solution. – Meligy Jan 24 '12 at 11:47
  • @RobG I got that error on Chrome as well. :/ Anyone know how to make this work? – Nathan Sep 30 '12 at 03:54
  • Did you try the jsfiddle link the answer? It's still working for me in Chrome 23 Dev Channel. – Meligy Oct 01 '12 at 02:38
  • I get this error: `Uncaught Error: Syntax error, unrecognized expression: unsupported pseudo: hover`. Apparently an issue with jQuery 1.9+. – allicarn Jun 27 '13 at 17:25
  • It seems to work in 2.0.2 and 1.10.1 as I tested after your comment. Thanks a lot for the note. I updated the answer now to reflect that. – Meligy Sep 21 '13 at 13:20
  • This works great if you have `selectable` set to false. If you your calendar requires selection, the `.fc-cell-overlay` div that displays the selection is positioned over the numbers, and so `.is(':hover')` returns false. I have yet to find a way around this, unfortunately. – Katie Kilian Jan 31 '14 at 15:51
  • @Wilmer check my 3rd edit above re: your problem. Thanks for helping me understand how this works a bit more. – Meligy Mar 08 '14 at 23:54
  • One other thing to note: ':hover' is updated on the mouseenter/mouseleave events. If you are in the event handler for mouseleave of one element, the element the mouse just entered may not have ':hover' applied to it yet since the mouseenter event hasn't fired for that element yet so you might need to set a timeout before checking the updated status of ':hover' on the element the mouse was moved into. – Farrah Stark May 05 '14 at 20:31
  • 1
    @Meligy: I've forked your fiddle to illustrate that it's not working with multiple items in the selection: http://jsfiddle.net/p34n8/ – SuperNova May 08 '14 at 11:23
  • 1
    Also if you are using jQuery mobile, that screws it up aswell- check the fiddle in gideons answer and turn the jQuery Mobile 1.4.4 box on and you can see it doesn't work then... I can confirm its the same with 1.4.2 aswell :( – David O'Sullivan Dec 16 '14 at 18:07
  • Exactly, I was in the middle of v writing about that mistake when work stuff came up. By the time I can't back there were other answers highlighting it too. Good for the OP :) – Meligy Dec 16 '14 at 22:06
  • Unfornunately it doesn't work for Windows Phone's 8.1 IE Browser no matter which method or which jQuery Version is used... :( – user2718671 Mar 29 '16 at 11:02
  • The concept of hover is not mobile friendly in general. It kinda works sometimes (sometimes it's triggered by long tap or so), but it's inconsistent and just broken one way or another on mobiles. – Meligy Mar 29 '16 at 12:49
35

Use:

var hovered = $("#parent").find("#element:hover").length;

jQuery 1.9+

user2444818
  • 351
  • 3
  • 2
  • 1
    This works brilliantly well. Best solution found while browsing for a replacement for .hover function and :hover selector. Thank you! – Tyler Sep 02 '13 at 22:24
9

It does not work in jQuery 1.9. Made this plugin based on user2444818's answer.

jQuery.fn.mouseIsOver = function () {
    return $(this).parent().find($(this).selector + ":hover").length > 0;
}; 

http://jsfiddle.net/Wp2v4/1/

allicarn
  • 2,859
  • 2
  • 28
  • 47
6

The accepted answer didn't work for me on JQuery 2.x .is(":hover") returns false on every call.

I ended up with a pretty simple solution that works:

function isHovered(selector) {

    return $(selector+":hover").length > 0

}
gidim
  • 2,314
  • 20
  • 23
5

Set a flag on hover:

var over = false;
$('#elem').hover(function() {
  over = true;
},
function () {
  over = false;
});

Then just check your flag.

kinakuta
  • 9,029
  • 1
  • 39
  • 48
  • Simple, but not easily reused. :/ – Trevor Jan 24 '12 at 03:00
  • Didn't seem to work in my particular situation, but I agree that's a good way to go otherwise. In my case, when I `mouseleave` one element, I want to check to see if the mouse entered another particular element. – James Skidmore Jan 24 '12 at 03:01
  • @JamesSkidmore - in the mouseleave event you can use `e.fromElement` and `e.toElement`. Will that work for you? – mrtsherman Jan 24 '12 at 03:03
  • You could update this to use a selector that covers all of the applicable elements, and store the flag on the individual elements using `.data()`. – nnnnnn Jan 24 '12 at 03:03
  • @Trevor - true - you'd need to store the state on the element then create a function that checks (presumably data-tag) state of the element. – kinakuta Jan 24 '12 at 03:04
4

Couple updates to add after working on this subject for a while:

  1. all solutions with .is(":hover") break on jQuery 1.9.1
  2. The most likely reason to check if the mouse is still over an element is to attempt to prevent events firing over each other. For example, we were having issues with our mouseleave being triggered and completed before our mouseenter event even completed. Of course this was because of a quick mouse movement.

We used hoverIntent https://github.com/briancherne/jquery-hoverIntent to solve the issue for us. Essentially it triggers if the mouse movement is more deliberate. (one thing to note is that it will trigger on both mouse entering an element and leaving - if you only want to use one pass the constructor an empty function )

Donald A Nummer Jr
  • 1,137
  • 9
  • 11
4

You can filter your elment from all hovered elements. Problematic code:

element.filter(':hover')

Save code:

jQuery(':hover').filter(element)

To return boolean:

jQuery(':hover').filter(element).length===0
Mino
  • 973
  • 7
  • 14
3

Expanding on @Mohamed's answer. You could use a little encapsulation

Like this:

jQuery.fn.mouseIsOver = function () {
    if($(this[0]).is(":hover"))
    {
        return true;
    }
    return false;
}; 

Use it like:

$("#elem").mouseIsOver();//returns true or false

Forked the fiddle: http://jsfiddle.net/cgWdF/1/

gideon
  • 19,329
  • 11
  • 72
  • 113
1

I like the first response, but for me it's weird. When attempting to check just after page load for the mouse, I have to put in at least a 500 millisecond delay for it to work:

$(window).on('load', function() {
    setTimeout(function() {
        $('img:hover').fadeOut().fadeIn();
    }, 500);
});

http://codepen.io/molokoloco/pen/Grvkx/

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
molokoloco
  • 4,504
  • 2
  • 33
  • 27
1

https://api.jquery.com/hover/

Asynchronous function in line 38:

$( ".class#id" ).hover(function() {
  Your javascript
});
Leon
  • 255
  • 3
  • 12
0
There're so many ways this can be achieved. I personally don't like styling elements directly, I'd rather add the style from css, makes things easier when trying to override or change them.

<style type="text/css">.red-bg { background: red; }</style>

$('#my_element').hover(function() {
    $(this).parent().toggleClass('red-bg');
});

OR

$('#my_element').hover(function() {
    $(this).parent().addClass('red-bg');
}, function() {
    $(this).parent().removeClass('red-bg');
});

OR using mouse enter and mouse leave

$(document).on('mouseenter', '#my_element', function() {
    $(this).parent().addClass('red-bg');
});

$(document).on('mouseenter', '#my_element', function() {
    $(this).parent().removeClass('red-bg');
});
Dexter
  • 74
  • 8
0

Setting a flag per kinakuta's answer seems reasonable, you can put a listener on the body so you can check if any element is being hovered over at a particular instant.

However, how do you want to deal with child nodes? You should perhaps check if the element is an ancestor of the currently hovered element.

<script>

var isOver = (function() {
  var overElement;
  return {

    // Set the "over" element
    set: function(e) {
      overElement = e.target || e.srcElement;
    },

    // Return the current "over" element
    get: function() {
      return overElement;    
    },

    // Check if element is the current "over" element
    check: function(element) {
      return element == overElement;
    },

    // Check if element is, or an ancestor of, the 
    // current "over" element
    checkAll: function(element) {
      while (overElement.parentNode) {
         if (element == overElement) return true;
         overElement = overElement.parentNode;
      }
      return false;
    }
  };
}());


// Check every second if p0 is being hovered over
window.setInterval( function() {
  var el = document.getElementById('p0');
  document.getElementById('msg').innerHTML = isOver.checkAll(el);
}, 1000);


</script>

<body onmouseover="isOver.set(event);">
  <div>Here is a div
    <p id="p0">Here is a p in the div<span> here is a span in the p</span> foo bar </p>
  </div>
  <div id="msg"></div>
</body>
RobG
  • 142,382
  • 31
  • 172
  • 209