15

You can use this to find a document element at a given point

document.elementFromPoint(x, y);

What can you do if there are overlapping elements at the point? (I know this is not a great way to do things -- trying a hackish workaround for a bug before a deadline).

bernie2436
  • 22,841
  • 49
  • 151
  • 244

6 Answers6

29

If you want to find all the DOM elements that overlap over a point, you can simply use document.elementsFromPoint(), which returns an array of all elements found in said point (apparently, ordered from top to bottom relative to the viewport, i.e.: if there's an overlay, it will show up first in the array)

Sebastianb
  • 2,020
  • 22
  • 31
24

As I think you already know, document.elementFromPoint(x, y); only returns the top-most element that overlaps that point.

If what you're trying to do is find all elements that overlap with a given point, even elements behind other elements, then I'm not aware of any DOM function that will do that for you. You may have to write your own.

A somewhat hackish version would be to call elementFromPoint(x,y), remember that DOM item, then hide that item with display: none, then call elementFromPoint(x,y) again until all you get is the body, then restore the items you hid.

A less hackish version would be to cycle though all objects in the page and compare their offset/height/width in the page to your point.

Here's one way to do it:

function getAllElementsFromPoint(x, y) {
    var elements = [];
    var display = [];
    var item = document.elementFromPoint(x, y);
    while (item && item !== document.body && item !== window && item !== document && item !== document.documentElement) {
        elements.push(item);
        display.push(item.style.display);
        item.style.display = "none";
        item = document.elementFromPoint(x, y);
    }
    // restore display property
    for (var i = 0; i < elements.length; i++) {
        elements[i].style.display = display[i];
    }
    return elements;
}

Working demo: http://jsfiddle.net/jfriend00/N9pu9/

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • thanks a lot. super helpful in resolving a nasty bug – bernie2436 Mar 15 '14 at 20:17
  • 5
    A better hack to this could be this - https://gist.github.com/Rooster212/4549f9ab0acb2fc72fe3. It uses pointer-events rather than display none. – Ashish Singh Mar 08 '16 at 05:05
  • 4
    Thanks everyone, and, btw - as of August 2018 most browsers now supports elementsFromPoint method (note, this is element**s**) - https://developer.mozilla.org/en-US/docs/Web/API/DocumentOrShadowRoot/elementsFromPoint - which already is addressed in the above gist code by ashish as well. – konrados Aug 21 '18 at 09:11
  • 1
    i didn't even know that `elementFromPoint` kind of thing exits ! thanks – Dhaval Pankhaniya Nov 23 '18 at 14:41
  • Using `pointer-events: none` is sufficient for accessing the element under the current one. I wish there was a native method for it. – malipetek Jun 18 '23 at 16:41
6

Not sure for what reason the initial question was asked, but if you need to detect an element under currently draggable with touch or mouse element, you can set pointer-events: none; on the draggable element. When you do this, document.elementFromPoint() will ignore currently draggable element and return the one underneath it.

zmechanic
  • 1,842
  • 21
  • 27
  • 2
    Thanks, I've been struggling with getting ```document.elementFromPoint()``` to behave when dragging and this resolved the issue. – nevf Nov 13 '18 at 06:22
  • In Javascript, `element.style.pointerEvents = 'none'`, and after getting the one underneath, `element.style.pointerEvents = 'auto'` – quilkin Apr 10 '23 at 19:27
  • also see my answer below, using `hidden` – quilkin Apr 10 '23 at 19:34
2

So from what I can see, this answer - https://stackoverflow.com/a/54350762/479836 - would be the ideal, but unfortunately, document.elementsFromPoint() does not work in IE11.

Failing that, the code in this answer - https://stackoverflow.com/a/22428553/479836 appears to work across most modern browsers, including IE11, but there's a bug where certain DOM elements are duplicated in the array returned by the function.

However, if anyone's looking for an document.elementsFromPoint() shim, a good one can be found here:

https://gist.github.com/oslego/7265412

Copied here for convenience:

function elementsFromPoint(x,y) {
    var elements = [], previousPointerEvents = [], current, i, d;

        // get all elements via elementFromPoint, and remove them from hit-testing in order
    while ((current = document.elementFromPoint(x,y)) && elements.indexOf(current)===-1 && current != null) {

            // push the element and its current style
        elements.push(current);
        previousPointerEvents.push({
                value: current.style.getPropertyValue('pointer-events'),
                priority: current.style.getPropertyPriority('pointer-events')
            });

            // add "pointer-events: none", to get to the underlying element
        current.style.setProperty('pointer-events', 'none', 'important'); 
    }

        // restore the previous pointer-events values
    for(i = previousPointerEvents.length; d=previousPointerEvents[--i]; ) {
        elements[i].style.setProperty('pointer-events', d.value?d.value:'', d.priority); 
    }

        // return our results
    return elements;
}
Charlie
  • 4,197
  • 5
  • 42
  • 59
0

Simple Solution

In case $e is the overlapping element, e is the event

$e.style.display = 'none';
var b = document.elementFromPoint(e.clientX,e.clientY);
$e.style.display = 'block';
0

Another solution for use while dragging

const clientX = e.clientX || e.changedTouches[0].clientX;
const clientY = e.clientY || e.changedTouches[0].clientY;

e.target.hidden = true;
const elemBelow = document.elementFromPoint(clientX, clientY);
e.target.hidden = false;
quilkin
  • 874
  • 11
  • 31