15

On user click I would like to get a list of all elements that resides at the clicked point.

For example, if user clicks on Hello here:

<div><span>Hello<span></div>

I would like to get the following list:

  • <span> element
  • <div> element
  • <body> element
  • <html> element

What would be the easiest method to get these elements ?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Misha Moroshko
  • 166,356
  • 226
  • 505
  • 746

8 Answers8

12

EDIT: Based on clarification, I think this is what you mean:

EDIT: As pointed out by @Misha, outerWidth() and outerHeight() should be used in lieu of width() and height() in order to get an accurate range.

Also, if there's nothing to prevent event bubbling on the page, then the click should be placed on the document as it will be much more efficient. Even if some other click handler prevents bubbling, you should still have the click on the document, and just handle it separately from those handler that prevent bubbling.

Example: http://jsfiddle.net/57bVR/3/

$(document).click(function(e) {
    var clickX = e.pageX
        ,clickY = e.pageY
        ,list
        ,$list
        ,offset
        ,range
        ,$body = $('body').parents().andSelf();

    $list = $('body *').filter(function() {
        offset = $(this).offset();
        range = {
            x: [ offset.left,
                offset.left + $(this).outerWidth() ],
            y: [ offset.top,
                offset.top + $(this).outerHeight() ]
        };
        return (clickX >= range.x[0] && clickX <= range.x[1]) && (clickY >= range.y[0] && clickY <= range.y[1])
    });

    $list = $list.add($body);

    list = $list.map(function() {
        return this.nodeName + ' ' + this.className
    }).get();
    alert(list);
    return false;
});​

Original answer:

This will give you an Array of the tag names including the span. Couldn't quite tell if this is what you wanted.

It uses .parents() along with .andSelf() to get the elements, then uses .map() with .get() to create the Array.

Example: http://jsfiddle.net/9cFTG/

var list;

$('span').click(function() {
    list = $(this).parents().andSelf().map(function() {
        return this.nodeName;
    }).get();
    alert(list);
});​

If you just wanted the elements, not the tag names, get rid of .map() and .get().

Or if you wanted to join the Array into a String using some sort of separator, just add .join(" ") after .get(), placing your separator inside the quotes.

user113716
  • 318,772
  • 63
  • 451
  • 440
  • I'm not looking for parents. Look here: http://jsfiddle.net/9cFTG/1/ `.a` and `.b` are not parents, but if user clicks on the intersection, I would like to have them both ! (sorry, fixed the link) – Misha Moroshko Sep 17 '10 at 12:42
  • @Misha - So what you're looking for is a list of all the elements that are positioned via CSS at the point on the screen where the click took place? It would seem that you would need to loop through *every* element on the page and get its position and size, and compare it to the click position. Are you sure that's what you want? Or can you limit it to certain classes of elements? – user113716 Sep 17 '10 at 12:48
  • I want let user to change elements colors on the page. When he clicks on some element, in case of ambiguity, he should be able to choose which element he meant. – Misha Moroshko Sep 17 '10 at 12:51
  • @Misha - Give me a couple minutes. – user113716 Sep 17 '10 at 12:54
  • @Misha - Before I post it, I want to see if this is what you mean. http://jsfiddle.net/57bVR/1/ It does not include the `` and `` elements. If you want to include those, we could probably just add them to the set manually since they should always be included. – user113716 Sep 17 '10 at 13:04
  • @Misha - You're welcome. I posted the answer, and added in the `` and ``. Also, if there will not be any elements added dynamically to the page, it would probably be good to cache `$('body *')` in a variable so you're not having traverse the DOM every time. :o) – user113716 Sep 17 '10 at 13:20
  • One last comment: I think it is better to use `outerWidth()` and `outerHeight()` to include also the possible borders. – Misha Moroshko Sep 19 '10 at 12:05
  • @Misha - Excellent point! Since the values returned from `offset()` include the border, if you don't use `outerWidth/Height`, then the "range" will be off, and clicks on the right/bottom edges (even inside the borders) won't register. One more thing. As long as you don't have any other 'click' events on the page that prevent event bubbling, it will be *much* more efficient to place the `click` event on the `document`, as in `$(document).click(...`. Not sure why I didn't mention it before. http://jsfiddle.net/57bVR/3/ I'll update my answer with both changes for future reference. :o) – user113716 Sep 19 '10 at 13:06
  • If anyone is interested: I needed this to be more dynamic and had to reuse the code multiple times so I turned it into a jQuery Plugin that can be downloaded here: https://github.com/SanderSoulwax/UnderPoint. (Note: this is my first jQuery Plugin so there might be some things that could've been done better) – Terrabythia Apr 25 '13 at 13:47
11

In the near future this should be possible:

$(document).click(function(e) {
    var family = this.elementsFromPoint(e.pageX, e.pageY);
    $(family).each( function () {
            console.log(child);
    });
});

Update 2019
Currently in editors draft:
Elements from point

Persijn
  • 14,624
  • 3
  • 43
  • 72
  • Marked as not yet widely implemented, but does work with Chrome 53 (10/1/2016). – user35443 Oct 02 '16 at 14:26
  • 1
    Definitely a few, it's marked as "experimental technology", fully supported on Chrome 43+, Firefox 46+, experimental on IE, unknown on Opera and not supported by Safari. – user35443 Jan 06 '17 at 18:17
0

The jQuery parents() function can do this for you.

To attach a click event to all span tags, for example:

$("span").click(function() {
    var parents = "";
    $(this).parents().map(function () {
        parents = parents + " " + this.tagName;
    })
    alert(parents);
});
Jeff
  • 21,744
  • 6
  • 51
  • 55
0

use parent method to get parent of the current tag recursively or in cycle:

var parentTag = $(this).parent().get(0).tagName;
Andy E
  • 338,112
  • 86
  • 474
  • 445
zaynyatyi
  • 1,116
  • 6
  • 18
0

Try something like this

$('span').click(function() {
 var parents = $(this).parents();
 for(var i = 0; i < parents.length; i++){
  console.log($(parents[i]).get(0).tagName)
 }
});

check the live demo

http://jsfiddle.net/sdA44/1/

Pramendra Gupta
  • 14,667
  • 4
  • 33
  • 34
0

Just javascript implementation:

window.addEventListener('click', function(e){
    console.log(document.elementFromPoint(e.pageX, e.pageY))
}, false)

Worked for me in firefox and chrome as of 3-29-2017

jmunsch
  • 22,771
  • 11
  • 93
  • 114
0

Found this: https://gist.github.com/osartun/4154204

Had to change elements.get(i) to elements[i] to fix it...

plavozont
  • 807
  • 9
  • 17
-1

I have updated the demo http://jsfiddle.net/57bVR/3/, adding to the code the logic to manage also (if present):

  • The scrolling of the page
  • The zoom level html tag (in the project I work it gives to me several troubles)


$(document).click(function (e) {
    var htmlZoom = window.getComputedStyle(document.getElementsByTagName('html')[0]).getPropertyValue('zoom');
    var clickX = e.pageX,
    clickY = e.pageY,
    list,
    $list,
    offset,
    range,
    $body = $('body').parents().andSelf();
    $list = $('body *').filter(function () {
            offset = $(this).offset();
            marginLeft = offset.left * htmlZoom - $(document).scrollLeft();
            marginTop = offset.top * htmlZoom - $(document).scrollTop();
            outerWidth = $(this).outerWidth() * htmlZoom;
            outerHeight = $(this).outerHeight() * htmlZoom;
            range = {
                x : [marginLeft,
                    marginLeft + outerWidth],
                y : [marginTop,
                    marginTop + outerHeight]
            };

        return (clickX >= range.x[0] && clickX <= range.x[1]) && (clickY >= range.y[0] && clickY <= range.y[1])

    });

$list = $list.add($body);

list = $list.map(function () {
    return this.nodeName + ' ' + this.className
}).get();
alert(list);
return false;
});