Como verifico se o mouse está sobre um elemento no jQuery?

265

Existe uma maneira rápida e fácil de fazer isso no jQuery que estou perdendo?

Não quero usar o evento mouseover porque já o estou usando para outra coisa. Eu só preciso saber se o mouse está sobre um elemento em um determinado momento.

Eu gostaria de fazer algo assim, se ao menos houvesse uma função "IsMouseOver":

function hideTip(oi) {
    setTimeout(function() { if (!IsMouseOver(oi)) $(oi).fadeOut(); }, 100);
}
Steve Wortham
fonte
5
Para a maioria das finalidades, as respostas dadas são suficientes, mas há casos em que o mouse / out não é suficiente. Por exemplo, ocultar um menu quando o mouse não estiver mais sobre o cabeçalho ou o corpo do menu.
Marcus Downing
Usei o método descrito na minha resposta para trabalhar com ícones (eventos de mouse para bordas de botões) que abrem suspensos animados e atrasados ​​no fechamento. Você lida com o atraso / cancelamento no ícone e no menu suspenso usando os métodos triggerHandler do jquery. Totalmente suficiente.
Mothmonsterman 13/08/09
#Marcus: Se esconder um menu, qual é a melhor maneira de fazer isso?
Coderama 8/09/10
Veja minha resposta stackoverflow.com/questions/9827095/…
Sanne
Eu teria votado a favor se a melhor resposta tivesse sido marcada como a solução.
BBaysinger # 25/18

Respostas:

97

Defina um tempo limite no mouseout para desaparecer e armazenar o valor retornado para os dados no objeto. Em seguida, após a transição, cancele o tempo limite se houver um valor nos dados.

Remova os dados no retorno de chamada do fadeout.

Na verdade, é mais barato usar o mouseenter / mouseleave porque eles não são acionados no menu quando as crianças passam o mouse / mouseout.

mothmonsterman
fonte
7
@ Arthur fez aqui, você ainda precisa de mais informações? stackoverflow.com/a/1670561/152640
mothmonsterman
270

Este código ilustra o que happytime harry e eu estamos tentando dizer. Quando o mouse entra, sai uma dica de ferramenta, quando o mouse sai, define um atraso para que desapareça. Se o mouse digitar o mesmo elemento antes que o atraso seja acionado, destruiremos o gatilho antes que ele dispare usando os dados que armazenamos anteriormente.

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

AVISO: is(':hover')foi descontinuado no jquery 1.8+. Veja esta postagem para uma solução.

Você também pode usar esta resposta: https://stackoverflow.com/a/6035278/8843 para testar se o mouse está passando o mouse sobre um elemento:

$('#test').click(function() {
    if ($('#hello').is(':hover')) {
        alert('hello');
    }
});
tal
fonte
5
Isto não é documentado em qualquer lugar (Afik) e não parece ser preciso com elementos mostrados dinamicamente (como um menu) ..
lambinator
12
quebrado a partir do jQuery 1.9.1 !! use a solução do Ivo em vez disso
mathheadinclouds
1
Erro não capturado: Erro de sintaxe, expressão não reconhecida: pseudo: hover não suportado
Julio Marins
1
Aviso : :hovernão é um seletor jQuery válido: api.jquery.com/category/selectors (fonte: bugs.jquery.com/ticket/11574 )
Pang
1
Ele ainda funciona se os suportes do navegadordocument.querySelectorAll(':hover')
ekuusela
34

Você pode usar o hoverevento do jQuery para acompanhar manualmente:

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

if ($(something).data('hover'))
    //Hovered!
SLaks
fonte
1
por que usar data () e não add / removeclass ()? Um é mais eficiente que o outro?
Psychotik
2
@psychotik: Sim; $.datanão envolve manipulação de strings.
SLaks
Eu envolvi isso em uma classe: stackoverflow.com/questions/1273566/...
ripper234
24

Eu precisava de algo exatamente assim (em um ambiente um pouco mais complexo e a solução com muitos 'mouseenters' e 'mouseleaves' não funcionavam corretamente), então criei um pequeno plugin de jquery que adiciona o método ismouseover. Até agora, funcionou muito bem.

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

Em qualquer lugar do documento, você o chama assim e retorna verdadeiro ou falso:

$("#player").ismouseover()

Eu testei no IE7 +, Chrome 1+ e Firefox 4 e está funcionando corretamente.

Ivan Castellanos
fonte
Ele não parece trabalhar em mouseenter (Chrome) - codepen.io/anon/pen/kcypB
wrygiel
Perfeito. Expressão de função invocada imediatamente (IIFE) que resolve o problema de direcionar objetos sob o elemento com sobreposição de opacidade. Brilhante! Obrigado por isso.
Alexander Dixon
10

No jQuery você pode usar .is (': hover'), então

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

agora seria a maneira mais concisa de fornecer a função solicitada no OP.

Nota: O acima não funciona no IE8 ou inferior

Como alternativa menos sucinta que funciona no IE8 (se eu posso confiar no módulo IE8 do IE9), e o faz sem acionar $(...).hover(...)por todo o lado, nem requer conhecer um seletor para o elemento (nesse caso, a resposta do Ivo é mais fácil):

function IsMouseOver(oi)
{
    return oi.length && 
           oi.parent()
             .find(':hover')
             .filter(function(s){return oi[0]==this})
             .length > 0;
}
towr
fonte
Este não é um seletor jQuery válido! As pessoas têm que parar de sugerir esse método. Está em toda parte e não é compatível com o IE8.
Sanne
Veja minha outra resposta para ter uma solução para o IE8
Sanne
2
@Sanne É curioso, porque $(':hover') faz o trabalho no IE8. É um pseudo-seletor CSS2 válido, por isso deve funcionar.
Towr
7

Peguei a ideia de SLaks e envolvi-a em uma turma pequena .

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

APENAS FYI para futuros descobridores disso.

Eu criei um plugin jQuery que pode fazer isso e muito mais. No meu plugin, para obter todos os elementos sobre os quais o cursor está passando o mouse, basta fazer o seguinte:

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

Como mencionei, ele também tem muitos outros usos, como você pode ver no

jsFiddle encontrado aqui

SpYk3HH
fonte
5

Como não posso comentar, escreverei isso como resposta!

Por favor, entenda a diferença entre o seletor css ": hover" e o evento hover!

": hover" é um seletor de css e foi de fato removido com o evento quando usado assim $("#elementId").is(":hover"), mas significa que ele realmente não tem nada a ver com o hover de evento do jQuery.

se você codificar $("#elementId:hover"), o elemento só será selecionado quando você passar o mouse com o mouse. a instrução acima funcionará com todas as versões do jQuery à medida que você seleciona esse elemento com a seleção pura e legítima do css.

Por outro lado, o evento passa o mouse que é

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

é realmente preterido como jQuery 1.8 aqui o estado do site jQuery:

Quando o nome do evento "pairar" é usado, o subsistema de eventos o converte em "mouseenter mouseleave" na cadeia de eventos. Isso é irritante por vários motivos:

Semântica: pairar não é o mesmo que o mouse que entra e sai de um elemento, implica uma certa desaceleração ou atraso antes do disparo. Nome do evento: O event.type retornado pelo manipulador anexado não passa o mouse, mas sim o mouseenter ou mouseleave. Nenhum outro evento faz isso. Cooptando o nome "hover": Não é possível anexar um evento com o nome "hover" e dispará-lo usando .trigger ("hover"). Os documentos já chamam esse nome de "fortemente desencorajado por novo código". Gostaria de desaprová-lo oficialmente para 1.8 e eventualmente removê-lo.

Por que eles removeram o uso não é claro (": hover"), mas tudo bem, você ainda pode usá-lo como acima e aqui está um pequeno truque para usá-lo.

(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, e eu não recomendaria a versão do timeout, pois isso traz muita complexidade , use as funcionalidades de timeout para esse tipo de coisa se não houver outra maneira e acredite: em 95% dos casos, existe outra maneira !

Espero poder ajudar algumas pessoas por aí.

Greetz Andy

Phenix
fonte
2

Graças a vocês dois. Em algum momento, tive que desistir de tentar detectar se o mouse ainda estava sobre o elemento. Eu sei que é possível, mas pode exigir muito código para ser realizado.

Demorei um pouco, mas aceitei as duas sugestões e criei algo que funcionaria para mim.

Aqui está um exemplo simplificado (mas 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);
    }
);

E então, para fazer isso funcionar em algum texto, é tudo o que tenho que fazer:

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

Juntamente com um monte de CSS sofisticados, isso permite algumas dicas muito úteis sobre a ajuda do mouse. A propósito, eu precisava do atraso na saída do mouse por causa de pequenas lacunas entre as caixas de seleção e o texto que estava causando a ajuda da intermitência à medida que você move o mouse. Mas isso funciona como um encanto. Também fiz algo semelhante para os eventos de foco / desfoque.

Steve Wortham
fonte
2

Eu vejo muito o tempo limite usado para isso, mas no contexto de um evento, você não consegue olhar para as coordenadas, assim:

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

Dependendo do contexto, pode ser necessário garantir (este == e.target) antes de chamar areXYInside (e).

fyi- Eu estou olhando para usar essa abordagem dentro de um manipulador dragLeave, para confirmar que o evento dragLeave não foi acionado ao entrar em um elemento filho. Se, de alguma forma, você não verificar se ainda está dentro do elemento pai, poderá tomar uma ação equivocada, destinada apenas a quando você realmente deixa o pai.

EDIT: esta é uma boa idéia, mas não funciona de forma consistente o suficiente. Talvez com alguns pequenos ajustes.

dlo
fonte
2

Você pode testar jQueryse algum div filho tem uma determinada classe. Em seguida, aplicando essa classe quando você passa o mouse por cima e por fora de uma determinada div, é possível testar se o mouse está sobre ela, mesmo quando você passa o mouse sobre um elemento diferente da página. Muito menos código dessa maneira. Usei isso porque tinha espaços entre divs em um pop-up e só queria fechar o pop-up quando saí do pop-up, não quando movia o mouse sobre os espaços no pop-up. Então, eu chamei uma função de mouseover na div de conteúdo (que a pop-up havia acabado), mas ela só acionaria a função close quando eu passava o mouse sobre a div de conteúdo E estava fora da pop-up!

$ (". pop-up"). mouseover (função (e)
    {
    $ (this) .addClass ("over");
    });

$ (". pop-up"). mouseout (função (e)
    {
    $ (this) .removeClass ("over");
    });


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

Nate
fonte
2

Essa seria a maneira mais fácil de fazer isso!

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

Aqui está uma técnica que não depende do jquery e usa a matches API DOM nativa . Ele usa prefixos de fornecedor para oferecer suporte a navegadores que retornam ao IE9. Veja matchesselector em caniuse.com para mais detalhes.

Primeiro, crie a função matchSelector, da seguinte maneira:

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

Em seguida, para detectar o foco:

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

Eu respondi isso em outra pergunta, com todos os detalhes que você pode precisar:

Detectar SE pairando sobre o elemento com o jQuery (possui 99 upvotes no momento da escrita)

Basicamente, você pode fazer algo como:

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

Isso funciona apenas se oifor um objeto jQuery que contém um único elemento. Se houver vários elementos correspondentes, você precisará aplicar a cada elemento, por exemplo:

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

Isso foi testado a partir do jQuery 1.7.

Meligy
fonte
1

Aqui está uma função que ajuda a verificar se o mouse está dentro de um elemento ou não. A única coisa que você deve fazer é chamar a função na qual você pode ter um EventObject associado ao mouse ao vivo. algo assim:

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

Você pode ver o código fonte aqui no github ou na parte inferior da postagem:

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

Estendendo o que 'Happytime harry' disse, certifique-se de usar a função .data () jquery para armazenar o ID do tempo limite. Isso é para que você possa recuperar o ID de tempo limite com muita facilidade quando o 'mouseenter' for acionado no mesmo elemento posteriormente, permitindo que você elimine o acionador para que a dica de ferramenta desapareça.

Arthur Goldsmith
fonte
0

Você pode usar os eventos mouseenter e mouseleave do jQuery. Você pode definir um sinalizador quando o mouse entra na área desejada e desmarcá-lo quando sai da área.

mikerobi
fonte
1
Era isso que eu estava pensando em fazer. Usar $ .data () como sugerido pelo SLaks parece ser uma boa maneira de conseguir isso.
JamesBrownIsDead
0

Combinei idéias deste tópico e criei isso, o que é útil para mostrar / ocultar um 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");
 });

Parece funcionar para mim. Espero que isso ajude alguém.

Edição: agora, percebendo essa abordagem não está funcionando corretamente no IE.

Dois cais
fonte
0

Não pude usar nenhuma das sugestões acima.
Por que prefiro minha solução?
Este método verifica se o mouse está sobre um elemento a qualquer momento escolhido por você .
Mouseenter e : hover são legais, mas o mouseenter é acionado apenas se você mover o mouse, não quando o elemento se move sob o mouse.
: pairar é muito doce, mas ... IE

Então eu faço isso:

Não 1. armazene a posição x, y do mouse toda vez que for movido quando necessário,
Não 2. verifique se o mouse está sobre algum dos elementos que correspondem à consulta, faça coisas ... como acionar um evento do 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
fonte
0

Apenas uma observação sobre a popular e útil resposta de Arthur Goldsmith acima: Se você estiver movendo o mouse de um elemento para outro no IE (pelo menos até o IE 9), poderá ter alguns problemas para que isso funcione corretamente se o novo elemento tiver um plano de fundo transparente (o que seria por padrão). Minha solução alternativa foi fornecer ao novo elemento uma imagem de plano de fundo transparente.

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

FIDDLE

yckart
fonte
-1

Você pode usar is(':visible');no jquery. E para $ ('. Item: hover'), ele também está funcionando no Jquery.

este é um snnipet de código 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>

e este é o 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');

});

é disso que eu estava falando :)

ucefkh
fonte
1
Não estou vendo como isso está relacionado à pergunta.
Andrew Barber
você pode usar isso quando você sair de mouseover e exibir o seu elemento oculto e depois de um atraso você pode verificar se ele é visível quando o mouse entra o elemento de destino que você deseja ocultar / mostrar
ucefkh
1
Acho que você não leu a pergunta muito bem. Isso não é o que ele precisa.
Andrew Barber
1
O que você está fazendo não está relacionado a esta pergunta . (aparentemente (
Andrew Barber