Tom Lehman
Tom Lehman

Reputation: 89373

Determine which element the mouse pointer is on top of in JavaScript

I want a function that tells me which element the mouse cursor is over.

So, for example, if the user's mouse is over this textarea (with id wmd-input), calling window.which_element_is_the_mouse_on() will be functionally equivalent to $("#wmd-input").

Upvotes: 166

Views: 175399

Answers (15)

Chris Kirby
Chris Kirby

Reputation: 155

I know this is an old question. I wanted to add my solution in.

function pointerOverElement( element, { clientX: x, clientY: y } ){
    const rect = element.getBoundingClientRect();
    return rect.left <= x && rect.right >= x && rect.top <= y && rect.bottom >= y;
}

then just use it in your events as:

pointerOverElement( element, e );

Upvotes: 1

qwertymk
qwertymk

Reputation: 35344

DEMO

There's a really cool function called document.elementFromPoint which does what it sounds like.

What we need is to find the x and y coords of the mouse and then call it using those values:

document.addEventListener('mousemove', e => {
  console.clear()
  console.log( document.elementFromPoint(e.clientX, e.clientY) )
}, {passive: true})
[class^='level']{
  width: 100px;
  height: 100px;
  padding: 15px;
  background: #00000033;
}
<div class='level-1'>
  <div class='level-2'>
    <div class='level-3'>
      Hover
    </div>
  </div>
</div>

document.elementFromPoint

jQuery event object

Upvotes: 221

Reid
Reid

Reputation: 4554

2022 Update:

document.elementsFromPoint() (Note the 's' in elements) is compatible with all major browsers. It basically does the same thing that elementFrompoint does, but retrieves all the elements in DOM order.

Mozilla has a good example of this:

HTML

<div>
  <p>Some text</p>
</div>
<p>Elements at point 30, 20:</p>
<div id="output"></div>

JavaScript

let output = document.getElementById("output");
if (document.elementsFromPoint) {
  let elements = document.elementsFromPoint(30, 20);
  for (var i = 0; i < elements.length; i++) {
    output.textContent += elements[i].localName;
    if (i < elements.length - 1) {
      output.textContent += " < ";
    }
  }
} else {
  output.innerHTML = "<span style=\"color: red;\">" +
     "Browser does not support <code>document.elementsFromPoint()</code>" +
     "</span>";
}

Output

Some text

Elements at point 30, 20:

p < div < body < html

https://developer.mozilla.org/en-US/docs/Web/API/Document/elementsFromPoint

Upvotes: 5

Zibri
Zibri

Reputation: 9857

Demo :D

Move your mouse in the snippet window :D

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

Upvotes: 6

herrlich10
herrlich10

Reputation: 6480

Although the following may not actually answering the question, since this is the first result of googling (the googler may not asking exactly the same question:), hope it will provide some extra input.

There are actually two different approaches to get a list of all elements the mouse is currently over (for newer browsers, perhaps):

The "structural" approach - Ascending DOM tree

As in dherman's answer, one can call

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

However, this assumes that only children will overlay their ancestors, which is usually the case, but not true in general, especially when dealing with SVG where element in different branches of the DOM tree may overlap each other.

The "visual" approach - Based on "visual" overlapping

This method uses document.elementFromPoint(x, y) to find the topmost element, temporarily hide it (since we recover it immediately in the same context, the browser will not actually renders this), then go on to find the second topmost element... Looks a little hacky, but it returns what you expect when there are, e.g., siblings elements in a tree occluding each other. Please find this post for more details,

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

Try both, and check their different returns.

Upvotes: 57

Philip Walton
Philip Walton

Reputation: 30461

Let me start out by saying that I don't recommend using the method I'm about to suggest. It's much better to use event driven development and bind events only to the elements you're interested in knowing whether or not the mouse is over with mouseover, mouseout, mouseenter, mouseleave, etc.

If you absolutely must have the ability to know which element the mouse is over, you'd need to write a function that binds the mouseover event to everything in the DOM, and then store whatever the current element is in some variable.

You could so something like this:

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

Basically, I've created an immediate function which sets the event on all elements and stores the current element within the closure to minimize your footprint.

Here's a working demo that calls window.which_element_is_the_mouse_on every second and logs what element the mouse is currently over to the console.

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

Upvotes: 0

saikumar
saikumar

Reputation: 1051

The following code will help you to get the element of the mouse pointer. The resulted elements will display in the console.

document.addEventListener('mousemove', function(e) {
    console.log(document.elementFromPoint(e.pageX, e.pageY)); 
})

Upvotes: 10

Serge Gordeev
Serge Gordeev

Reputation: 39

You can use this selector to undermouse object and then manipulate it as a jQuery object:

$(':hover').last();

Upvotes: 2

UZOR
UZOR

Reputation: 11

Here's a solution for those that may still be struggling. You want to add a mouseover event on the 'parent' element of the child element(s) you want detected. The below code shows you how to go about it.

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 on CodePen

Upvotes: 1

Karl Adler
Karl Adler

Reputation: 16836

elementFromPoint() gets only the first element in DOM tree. This is mostly not enough for developers needs. To get more than one element at e.g. the current mouse pointer position, this is the function you need:

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

This returns an array of all element objects under the given point. Just pass the mouse X and Y values to this function.

More information is here: DocumentOrShadowRoot.elementsFromPoint()

For very old browsers which are not supported, you may use this answer as a fallback.

Upvotes: 28

Ry-
Ry-

Reputation: 225281

You can look at the target of the mouseover event on some suitable ancestor:

var currentElement = null;

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

Here’s a demo.

Upvotes: 6

Max Heiber
Max Heiber

Reputation: 15582

The target of the mousemove DOM event is the top-most DOM element under the cursor when the mouse moves:

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

This is similar to @Philip Walton's solution, but doesn't require jQuery or a setInterval.

Upvotes: 1

Dip M
Dip M

Reputation: 71

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

Upvotes: 4

dherman
dherman

Reputation: 2892

In newer browsers, you could do the following:

document.querySelectorAll( ":hover" );

That'll give you a NodeList of items that the mouse is currently over in document order. The last element in the NodeList is the most specific, each preceding one should be a parent, grandparent, and so on.

Upvotes: 93

RobG
RobG

Reputation: 147523

Mouseover events bubble, so you can put a single listener on the body and wait for them to bubble up, then grab the event.target or event.srcElement:

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

<body onmouseover="doSomething(getTarget(event));">

Upvotes: 8

Related Questions