Determine sobre qué elemento está el puntero del mouse en Javascript

109

Quiero una función que me diga sobre qué elemento está el cursor del mouse.

Entonces, por ejemplo, si el mouse del usuario está sobre este área de texto (con id wmd-input), la llamada window.which_element_is_the_mouse_on()será funcionalmente equivalente a$("#wmd-input")

Tom Lehman
fuente

Respuestas:

147

MANIFESTACIÓN

Hay una función realmente genial llamada document.elementFromPointque hace lo que parece.

Lo que necesitamos es encontrar las coordenadas xey del mouse y luego llamarlo usando esos valores:

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

document.elementFromPoint

objeto de evento jQuery

qwertymk
fuente
1
@TikhonJelvis: Bueno, desde aquí veo que es compatible con IE y Firefox. Si obtienes esos dos, generalmente los obtienes todos. MSDN MDN
qwertymk
1
Increíble. ¿Hay alguna forma de obtener las coordenadas del mouse SIN capturar un evento? (Probablemente no supongo). Si esto no es posible, entonces desafortunadamente no creo que este método sea mejor que event.targeto lo que sea
Tom Lehman
1
@HoraceLoeb No es posible obtener las coordenadas del mouse sin capturar un evento. vea esta pregunta SO para obtener más explicaciones
Logan Besecker
2
Esa es una forma extraña de hacerlo. Si detecta un evento, ¿por qué no usarlo event.target?
pmrotule
3
La respuesta no explica qué eventes ni cómo llegó a ser
vsync
64

En los navegadores más nuevos, puede hacer lo siguiente:

document.querySelectorAll( ":hover" );

Eso le dará una NodeList de elementos sobre los que el mouse está actualmente en el orden del documento. El último elemento de la NodeList es el más específico, cada uno de los anteriores debe ser un padre, un abuelo, etc.

dherman
fuente
2
Esto no pareció funcionar mientras me arrastraba un <li>rato sobre otros <li>elementos.
Seiyria
2
Creé un violín con $(':hover')pero es básicamente lo mismo: jsfiddle.net/pmrotule/2pks4tf6
pmrotule
3
(function(){ var q = document.querySelectorAll(":hover"); return q[q.length-1]; })()
usuario
3
Tengo la sensación de que el rendimiento de esto es horrible ... llamar a esto mousemovepodría afectar el rendimiento
vsync
49

Aunque es posible que lo siguiente no responda realmente a la pregunta, ya que este es el primer resultado de la búsqueda en Google (es posible que el usuario no haga exactamente la misma pregunta :), espero que proporcione información adicional.

En realidad, hay dos enfoques diferentes para obtener una lista de todos los elementos sobre los que se encuentra actualmente el mouse (para los navegadores más nuevos, tal vez):

El enfoque "estructural": árbol DOM ascendente

Como en la respuesta de Dherman, uno puede llamar

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

Sin embargo, esto supone que solo los hijos se superpondrán a sus antepasados, lo que suele ser el caso, pero no es cierto en general, especialmente cuando se trata de SVG, donde los elementos en diferentes ramas del árbol DOM pueden superponerse entre sí.

El enfoque "visual": basado en la superposición "visual"

Este método utiliza document.elementFromPoint(x, y)para encontrar el elemento superior, ocultarlo temporalmente (ya que lo recuperamos inmediatamente en el mismo contexto, el navegador no lo renderizará), luego busca el segundo elemento superior ... Parece un poco hacky, pero devuelve lo que espera cuando hay, por ejemplo, elementos hermanos en un árbol ocluyéndose entre sí. Encuentre esta publicación para obtener más detalles,

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

Pruebe ambos y compruebe sus diferentes rendimientos.

herrlich10
fuente
Esto me ayudó mucho. Gracias @ herrlich10. Sorprendentemente, su código también maneja el caso con elementos secundarios anidados.
Vikram Deshmukh
Esto es muy útil. Exactamente lo que estaba buscando. Como dijiste, querySelectorAll no funciona en todos los escenarios.
noobsharp
4
@ herrlich10 Esto parece un polyfill decente para developer.mozilla.org/en-US/docs/Web/API/Document/… . Si esa API existe en su browserSet compatible, creo que podría usarla.
dherman
Gracias por esto, funciona prácticamente como se esperaba (aunque no hay elementos.reverse para compatibilidad con getElementsFromPoint), pero es mucho más lento (18-20ms por llamada en mis pruebas en Chrome) lo que hace que sea difícil de usar en el escenario que tuve en mente (encontrar objetivos de colocación durante un arrastre en un caso cuando se utiliza un enfoque más básico basado en eventos no es factible).
jsdw
1
ver Document.elementsFromPoint(x, y) stackoverflow.com/a/31805883/1059828
Karl Adler
21

elementFromPoint()obtiene solo el primer elemento en el árbol DOM. En su mayoría, esto NO es suficiente para las necesidades de los desarrolladores. Para obtener más de un elemento en, por ejemplo, la posición actual del puntero del mouse, esta es la función que necesita:

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

Esto devuelve una matriz de todos los objetos de elemento bajo el punto dado. Simplemente pase los valores X e Y del mouse a esta función.

Más info aquí: https://developer.mozilla.org/en-US/docs/Web/API/document/elementsFromPoint

Para navegadores muy antiguos que no son compatibles, puede usar esta respuesta como alternativa.

Karl Adler
fuente
3
Está bien respaldado ahora en 2018.
Chico
6

La burbuja de eventos al pasar el mouse sobre el mouse, por lo que puede poner un solo oyente en el cuerpo y esperar a que aparezcan, luego agarre event.targeto event.srcElement:

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

<body onmouseover="doSomething(getTarget(event));">
RobG
fuente
Si bien yo mismo uso controladores de eventos universales, esto sería más costoso en cuanto a recursos que document.elementFromPoint(x, y).
John
6

El siguiente código le ayudará a obtener el elemento del puntero del mouse. Los elementos resultantes se mostrarán en la consola.

document.addEventListener('mousemove', function(e) {
    console.log(document.elementFromPoint(e.pageX, e.pageY)); 
})
saikumar
fuente
1
Esto requiere que el cursor se mueva. Tan cerca como podría ser, no es lo que se pregunta aquí.
Carles Alcolea
No funciona si se desplaza la página. Utilice e.clientXy en su e.clientYlugar (probado en Firefox 59).
youen
5

Puede ver el objetivo del mouseoverevento en algún antepasado adecuado:

var currentElement = null;

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

Aquí tienes una demostración.

Ry-
fuente
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. -->
Sumergir M
fuente
3
Pido disculpas por la mala manera en que lo trataron en su pregunta (ahora eliminada). Personalmente, preferiría que no se eliminara la pregunta. Mientras estaba en camino de cerrarse (injustamente, en mi opinión), estaba planeando votar para reabrirlo. Sin embargo, dada la cantidad de votos negativos, entiendo que si decide mantenerla eliminada.
Halfer
3

DEMO: D Mueva el mouse en la ventana del fragmento: D

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

Zibri
fuente
1

El objetivo del mousemoveevento DOM es el elemento DOM más alto debajo del cursor cuando se mueve el mouse:

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

Esto es similar a la solución de @Philip Walton, pero no requiere jQuery o setInterval.

Max Heiber
fuente
1

Puede usar este selector para infravalorar el objeto y luego manipularlo como un objeto jquery. $(':hover').last();

Serge Gordeev
fuente
4
¡Bienvenido a Stack Overflow! Agregue una explicación de por qué este código ayuda al OP. Esto ayudará a proporcionar una respuesta de la que los futuros espectadores puedan aprender. Consulte Cómo responder para obtener más información. Además, "undermouse"?
Heretic Monkey
0

Permítanme comenzar diciendo que no recomiendo usar el método que estoy a punto de sugerir. Es mucho mejor usar evento impulsado el desarrollo y se unen sólo los eventos a los elementos de los que está interesado en saber si el ratón está sobre con mouseover, mouseout, mouseenter, mouseleave, etc.

Si absolutamente DEBE tener la capacidad de saber en qué elemento está el mouse, necesitaría escribir una función que vincule el mouseoverevento a todo en el DOM, y luego almacenar el elemento actual en alguna variable.

Podrías algo como esto:

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

Básicamente, he creado una función inmediata que establece el evento en todos los elementos y almacena el elemento actual dentro del cierre para minimizar su huella.

Aquí hay una demostración funcional que llama window.which_element_is_the_mouse_on cada segundo y registra qué elemento se encuentra actualmente en la consola.

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

Philip Walton
fuente
0

Pregunta vieja, pero aquí hay una solución para aquellos que todavía pueden estar luchando. Desea agregar un mouseoverevento en el elemento 'principal' de los elementos secundarios que desea detectar. El siguiente código le muestra cómo hacerlo.

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 EN CODEPEN

UZOR
fuente