如何使用jQuery检查鼠标是否悬停在元素上?

282

有没有一种在jQuery中快速且简单的方法来做到这一点,我错过了吗?

我不想使用mouseover事件,因为我已经在使用它做其他的事情。我只需要知道鼠标在特定时刻是否位于元素上。

我想做类似于这样的事情,如果只有一个"IsMouseOver"函数就好了:

function hideTip(oi) {
    setTimeout(function() { if (!IsMouseOver(oi)) $(oi).fadeOut(); }, 100);
}

5
大多数情况下,提供的答案是足够的,但在某些情况下,mousein/out并不足够。例如,在鼠标不再位于菜单头或菜单主体上时隐藏菜单。 - Marcus Downing
我已经使用了我在答案中描述的方法来处理图标(按钮边框的鼠标事件),以打开动画效果,延迟关闭的下拉菜单。你可以通过使用jQuery的triggerHandler方法来处理图标和下拉菜单中的延迟/取消延迟。非常充分。 - mothmonsterman
#Marcus:如果要隐藏菜单,最好的方法是什么? - rockstardev
请查看我的回答:https://dev59.com/RGkw5IYBdhLWcg3wirCs#21881898 - Sanne
如果最佳答案被标记为解决方案,我会投票支持这个回答。 - BBaysinger
26个回答

279

一个干净优雅的悬停检查:

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

6
没问题,这应该是最好的答案!无论如何,这里有一个相关的示例 http://jsfiddle.net/mathheadinclouds/ZKGqe/ - mathheadinclouds
如何检查元素是否被悬停:https://dev59.com/omox5IYBdhLWcg3ww3GV#8981521 - Kevin Wheeler
2
在Firefox和Chrome中表现不可靠。 - Savage

272

这段代码演示了我和happytime harry试图传达的意思。当鼠标进入时,会弹出一个提示框,当鼠标离开时,将设置一个延迟时间以使其消失。如果鼠标在延迟触发之前再次进入相同的元素,则我们使用之前存储的数据销毁触发器,以防它触发。

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

130

警告:在jquery 1.8+中,is(':hover')已被弃用。请查看此帖子以获取解决方案。

您也可以使用此答案:https://dev59.com/WVfUa4cB1Zd3GeqPGkor#6035278来测试鼠标是否悬停在一个元素上:

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

5
据我所知,这并没有被记录在任何地方,并且似乎对于动态显示的元素(例如菜单)也不准确。 - lambinator
13
自jQuery 1.9.1版开始已经不能使用,请改用Ivo的解决方案。 - mathheadinclouds
1
未捕获的错误:语法错误,无法识别的表达式:不支持的伪类:hover - Julio Marins
1
警告::hover 不是有效的 jQuery 选择器:http://api.jquery.com/category/selectors/(来源:http://bugs.jquery.com/ticket/11574) - Pang
1
如果浏览器支持 document.querySelectorAll(':hover'),它仍然可以工作。 - ekuusela
显示剩余4条评论

100
在mouseout事件上设置一个超时时间以进行淡出操作,并将返回值存储到对象的数据中。然后,在mouseover事件上,如果数据中存在值,则取消超时。在淡出回调函数中删除数据。
实际上,使用mouseenter/mouseleave更加节省资源,因为它们不会在子元素mouseover/mouseout事件触发时触发菜单的事件。

7
@Arthur 在这里做得很对,你还需要更多信息吗?https://dev59.com/2XM_5IYBdhLWcg3wrFMt#1670561 - mothmonsterman

35
你可以使用jQuery的hover事件来手动跟踪:
$(...).hover(
    function() { $.data(this, 'hover', true); },
    function() { $.data(this, 'hover', false); }
).data('hover', false);

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

1
为什么使用data()而不是add/removeclass()?哪一个更高效? - psychotik
2
@psychotik:是的;$.data不涉及字符串操作。 - SLaks
我将这个代码封装在一个类中:https://dev59.com/2XM_5IYBdhLWcg3wrFMt#7704678 - ripper234

24

我需要像这样的东西(在稍微复杂的环境中,使用大量的“鼠标进入”和“鼠标离开”解决方案无法正常工作),因此我创建了一个小的jQuery插件,添加了ismouseover方法。到目前为止,它的效果相当不错。

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

然后在文档的任何位置调用它,像这样它会返回true或false:

$("#player").ismouseover()

我已在IE7+、Chrome 1+和Firefox 4上测试过并且正常工作。


它似乎在mouseenter事件上不起作用(Chrome)- http://codepen.io/anon/pen/kcypB - wrygiel
太棒了。立即调用的函数表达式(IIFE)解决了针对带不透明度覆盖层的元素下面的对象的问题。太棒了!谢谢你。 - Alexander Dixon

9
在jQuery中,您可以使用`.is(':hover')`来判断鼠标是否悬停。
function IsMouseOver(oi)
{
   return $(oi).is(':hover');
}

现在提供的方法是在OP中请求的最简洁的方式。

注意:上面的方法在IE8或更低版本中不起作用。

作为一种不太简洁的选择,它可以在IE8中运行(如果我可以相信IE9的IE8模式),而且不会在所有地方触发$(...).hover(...),也不需要知道元素的选择器(在这种情况下,Ivo的答案更容易):

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

这不是有效的jQuery选择器!人们必须停止建议使用此方法。它无处不在,而且与IE8不兼容。 - Sanne
查看我的其他答案,以获取解决IE8的方案。 - Sanne
2
@Sanne 这很奇怪,因为 $(':hover') 在 IE8 中确实可以工作。这是一个有效的 CSS2 伪选择器,所以它应该可以工作。 - towr

7

我采用了SLaks的想法,并将其封装在一个小型类中。

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

6

由于我无法进行评论,因此我将把这个写成答案!

请理解css选择器“:hover”和悬停事件之间的区别!

“:hover”是一个css选择器,当像这样使用$("#elementId").is(":hover")时,它确实与事件一起被移除了,但在其意义上,它与jQuery事件悬停实际上没有任何关系。

如果您编写$("#elementId:hover"),只有在鼠标悬停时才会选择该元素。以上语句将在所有jQuery版本中工作,因为您正在使用纯正的css选择来选择此元素。

另一方面,悬停事件是

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

在jQuery网站上,确实已将"is(":hover")"废弃,以下是jQuery网站上的声明:

当使用事件名称"hover"时,事件子系统将其转换为事件字符串中的"mouseenter mouseleave"。这样做有以下几个问题:

语义:悬停不等于鼠标进入和离开元素,它意味着一定程度的减速或延迟才会触发。事件名称:附加处理程序返回的event.type不是hover,而是mouseenter或mouseleave。没有其他事件会这样做。占用"hover"名称:无法使用名称为"hover"的事件并使用.trigger("hover")触发它。文档已经将此名称称为"强烈不建议新代码使用",我想在1.8中正式弃用它,并最终删除它。

他们为什么删除了is(":hover")的使用原因不明,但是可以像上面那样继续使用它,这里有一个小技巧可以让你仍然使用它。

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

嗨,我不建议使用timeout版本,因为这样会带来很多复杂性。如果没有其他方法,请使用timeout功能来处理此类问题,并相信我,在95%的情况下,还有其他解决方案

希望我能帮助一些人。

问候 Andy


6

对于以后查找这篇文章的人,提供一些信息。

我制作了一个jQuery插件,可以做到这个并且更多。在我的插件中,要获取光标当前悬停在的所有元素,只需执行以下操作:

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

正如我所提到的,它还有许多其他用途,您可以在

这里找到的jsFiddle

中看到。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接