Determine em qual elemento o ponteiro do mouse está sobre o Javascript

109

Quero uma função que me diga sobre qual elemento o cursor do mouse está.

Assim, por exemplo, se o mouse do usuário estiver sobre esta textarea (com id wmd-input), a chamada window.which_element_is_the_mouse_on()será funcionalmente equivalente a$("#wmd-input")

Tom Lehman
fonte

Respostas:

147

DEMO

Existe uma função muito legal chamada document.elementFromPointque faz o que parece.

O que precisamos é encontrar as coordenadas xey do mouse e chamá-lo usando esses valores:

var x = event.clientX, y = event.clientY,
    elementMouseIsOver = document.elementFromPoint(x, y);

document.elementFromPoint

objeto de evento jQuery

qwertymk
fonte
1
@TikhonJelvis: Bem, a partir daqui , vejo que é compatível com IE e Firefox. Se você conseguir esses dois, geralmente terá todos. MSDN MDN
qwertymk
1
Impressionante. Existe alguma maneira de obter as coordenadas do mouse SEM capturar um evento? (Provavelmente não, eu suponho). Se isso não for possível, infelizmente não acho que esse método seja melhor do que event.targetou qualquer outra coisa
Tom Lehman
1
@HoraceLoeb Não é possível obter as coordenadas do mouse sem capturar um evento. veja esta pergunta SO para maiores explicações
Logan Besecker
2
É uma maneira estranha de fazer isso. Se você pegar um evento, por que não usar event.target?
pmrotule
3
A resposta não explica o que eventé e como veio a ser
vsync
64

Em navegadores mais recentes, você pode fazer o seguinte:

document.querySelectorAll( ":hover" );

Isso lhe dará uma NodeList de itens sobre os quais o mouse está atualmente na ordem do documento. O último elemento da NodeList é o mais específico, cada um que o precede deve ser um pai, avô e assim por diante.

dherman
fonte
2
Isso não pareceu funcionar enquanto eu arrastava um <li>pouco sobre os outros <li>elementos.
Seiyria
2
Eu criei um fiddle com, $(':hover')mas é basicamente a mesma coisa: jsfiddle.net/pmrotule/2pks4tf6
pmrotule
3
(function(){ var q = document.querySelectorAll(":hover"); return q[q.length-1]; })()
usuário
3
Tenho a sensação de que o desempenho disso é horrível .. ligar para isso mousemovepode prejudicar o desempenho
vsync
49

Embora o seguinte possa não responder de fato à pergunta, visto que este é o primeiro resultado da pesquisa no Google (o googler pode não fazer exatamente a mesma pergunta :), espero que ele forneça alguma entrada extra.

Na verdade, existem duas abordagens diferentes para obter uma lista de todos os elementos sobre os quais o mouse está atualmente (para navegadores mais recentes, talvez):

A abordagem "estrutural" - árvore DOM ascendente

Como na resposta do dherman, pode-se ligar

var elements = document.querySelectorAll(':hover');

No entanto, isso pressupõe que apenas os filhos irão sobrepor seus ancestrais, o que geralmente é o caso, mas não é verdade em geral, especialmente ao lidar com SVG, onde elementos em diferentes ramos da árvore DOM podem se sobrepor.

A abordagem "visual" - com base na sobreposição "visual"

Este método usa document.elementFromPoint(x, y)para encontrar o elemento superior, ocultá-lo temporariamente (já que o recuperamos imediatamente no mesmo contexto, o navegador não irá renderizá-lo de fato), então vá para encontrar o segundo elemento superior ... Parece um pouco estranho, mas ele retorna o que você espera quando há, por exemplo, elementos irmãos em uma árvore obstruindo um ao outro. Encontre esta postagem para mais detalhes,

function allElementsFromPoint(x, y) {
    var element, elements = [];
    var old_visibility = [];
    while (true) {
        element = document.elementFromPoint(x, y);
        if (!element || element === document.documentElement) {
            break;
        }
        elements.push(element);
        old_visibility.push(element.style.visibility);
        element.style.visibility = 'hidden'; // Temporarily hide the element (without changing the layout)
    }
    for (var k = 0; k < elements.length; k++) {
        elements[k].style.visibility = old_visibility[k];
    }
    elements.reverse();
    return elements;
}

Experimente ambos e verifique seus diferentes retornos.

herrlich10
fonte
Isso me ajudou muito. Obrigado @ herrlich10. Surpreendentemente, seu código lida com o caso com elementos filhos aninhados também.
Vikram Deshmukh
Isso é extremamente útil. Exatamente o que eu estava procurando. Como você disse, querySelectorAll não funciona em todos os cenários.
noobsharp
4
@ herrlich10 Parece um polyfill decente para developer.mozilla.org/en-US/docs/Web/API/Document/… . Se essa API existe em seu browserSet compatível, acho que você poderia usar isso.
dherman
Obrigado por isso - funciona quase como esperado (embora sem elementos.reverse para compatibilidade com getElementsFromPoint), mas é muito mais lento (18-20ms por chamada em meus testes em cromo), o que torna difícil de usar no cenário que tive em mente (encontrar alvos de soltar durante um arrasto em um caso ao usar uma abordagem mais básica orientada a eventos não é viável).
jsdw
1
consulte Document.elementsFromPoint(x, y) stackoverflow.com/a/31805883/1059828
Karl Adler
21

elementFromPoint()obtém apenas o primeiro elemento na árvore DOM. Isso NÃO é suficiente para as necessidades dos desenvolvedores. Para obter mais de um elemento, por exemplo, na posição atual do ponteiro do mouse, esta é a função de que você precisa:

document.elementsFromPoint(x, y) . // mind the 's' in elements

Isso retorna uma matriz de todos os objetos de elemento em um determinado ponto. Basta passar os valores X e Y do mouse para esta função.

Mais informações aqui: https://developer.mozilla.org/en-US/docs/Web/API/document/elementsFromPoint

Para navegadores muito antigos que não são suportados, você pode usar esta resposta como alternativa.

Karl Adler
fonte
3
É bem suportado agora em 2018.
Menino,
6

Passe o mouse sobre a bolha de eventos, para que você possa colocar um único ouvinte no corpo e esperar que eles borbulhem e, em seguida, pegue o event.targetou event.srcElement:

function getTarget(event) {
    var el = event.target || event.srcElement;
    return el.nodeType == 1? el : el.parentNode;
}

<body onmouseover="doSomething(getTarget(event));">
RobG
fonte
Embora eu mesmo use manipuladores de eventos universais, isso seria mais caro do que em termos de recursos document.elementFromPoint(x, y).
João de
6

O código a seguir o ajudará a obter o elemento do ponteiro do mouse. Os elementos resultantes serão exibidos no console.

document.addEventListener('mousemove', function(e) {
    console.log(document.elementFromPoint(e.pageX, e.pageY)); 
})
Saikumar
fonte
1
Isso requer que o cursor se mova. Por mais próximo que possa ser, não é o que está sendo perguntado aqui.
Carles Alcolea
Não funciona se a página for rolada. Use e.clientXe em seu e.clientYlugar (testado no Firefox 59).
youen
5

Você pode olhar para o alvo do mouseoverevento em algum ancestral adequado:

var currentElement = null;

document.addEventListener('mouseover', function (e) {
    currentElement = e.target;
});

Aqui está uma demonstração.

Ry-
fonte
4
<!-- One simple solution to your problem could be like this: -->

<div>
<input type="text" id="fname" onmousemove="javascript: alert(this.id);" />
<!-- OR -->
<input type="text" id="fname" onclick="javascript: alert(this.id);" />
</div>
<!-- Both mousemove over the field & click on the field displays "fname"-->
<!-- Works fantastic in IE, FireFox, Chrome, Opera. -->
<!-- I didn't test it for Safari. -->
Dip M
fonte
3
Peço desculpas pela maneira inadequada com que você foi tratado em sua pergunta (agora excluída). Pessoalmente, prefiro que a pergunta não seja excluída. Enquanto estava em vias de ser encerrado (injustamente, a meu ver), tencionava votar para o reabrir. No entanto, dado o número de votos negativos, entendo se você decidir mantê-lo excluído.
Halfer
3

DEMO: D Mova o mouse na janela de snippet: D

<script>
document.addEventListener('mouseover', function (e) {
	  console.log ("You are in ",e.target.tagName);
});
</script>

Zibri
fonte
1

O destino do mousemoveevento DOM é o elemento DOM mais alto sob o cursor quando o mouse se move:

(function(){
    //Don't fire multiple times in a row for the same element
    var prevTarget=null;
    document.addEventListener('mousemove', function(e) {
        //This will be the top-most DOM element under cursor
        var target=e.target;
        if(target!==prevTarget){
            console.log(target);
            prevTarget=target;
        }
    });
})();

Isso é semelhante à solução de @Philip Walton, mas não requer jQuery ou um setInterval.

Max Heiber
fonte
1

Você pode usar este seletor para undermouse objeto e então manipulá-lo como objeto jquery. $(':hover').last();

Serge Gordeev
fonte
4
Bem-vindo ao Stack Overflow! Adicione alguma explicação de por que esse código ajuda o OP. Isso ajudará a fornecer uma resposta com a qual os futuros espectadores podem aprender. Consulte Como responder para obter mais informações. Além disso, "undermouse"?
Macaco herege
0

Deixe-me começar dizendo que não recomendo usar o método que estou prestes a sugerir. É muito melhor usar impulsionado evento de desenvolvimento e se ligam eventos apenas para os elementos que você estiver interessado em saber se o mouse está sobre com mouseover, mouseout, mouseenter, mouseleave, etc.

Se você absolutamente DEVE ter a capacidade de saber qual elemento o mouse está sobre o qual está passando, você precisará escrever uma função que vincule o mouseoverevento a tudo no DOM e, em seguida, armazenar o que quer que o elemento atual esteja em alguma variável.

Você poderia fazer algo assim:

window.which_element_is_the_mouse_on = (function() {

    var currentElement;

    $("body *").on('mouseover', function(e) {
        if(e.target === e.currentTarget) {
            currentElement = this;
        }
    });

    return function() {
        console.log(currentElement);    
    }
}());

Basicamente, criei uma função imediata que define o evento em todos os elementos e armazena o elemento atual dentro do encerramento para minimizar sua pegada.

Aqui está uma demonstração de trabalho que chama a window.which_element_is_the_mouse_oncada segundo e registra qual elemento o mouse está atualmente sobre o console.

http://jsfiddle.net/LWFpJ/1/

Philip Walton
fonte
0

A pergunta antiga, mas aqui está uma solução para aqueles que ainda podem estar lutando. Você deseja adicionar um mouseoverevento ao elemento 'pai' do (s) elemento (s) filho (s) que deseja detectar. O código abaixo mostra como fazer isso.

const wrapper = document.getElementById('wrapper') //parent element
const position = document.getElementById("displaySelection")

wrapper.addEventListener('mousemove', function(e) {
  let elementPointed = document.elementFromPoint(e.clientX, e.clientY)

  console.log(elementPointed)
});

DEMO NO CODEPEN

UZOR
fonte