views:

183

answers:

3

Given absolute or relative position (top & left) is there any way to get the nearest html element to these co-ordinates?

Or alternately, is there any way to craft a selector (or use some jQuery construct) to enumerate elements and then find which is closes to the provided co-ordinates? Assume that the set of elements is small and finite.

+1  A: 

The best way I can think of to do this would be to have a search function loop that loops through all your existing elements and compares the co-ordinates, keeping a copy of the nearest variable all the time.

Thats the way I can think of doing this, and what I would do if I was under your constraints.

chustar
thanks... that's what I think/know will work, but would definitely prefer another solution is possible.
psychotik
@psychotik: Just make sure to not measure the dimensions of elements in the loop. Every measure triggers a browser reflow. Just `top` and `left` is fine I think.
Chetan Sastry
+1  A: 

Looping through all elements is pretty easy:

function getClosestElement(x, y) {
    var elements, el, offset, dist, i, minDist, closestEl;

    elements = $("body *");
    closestEl = elements.eq(i);
    offset = closestEl.offset();
    offset.left += el.outerWidth()/2; // center of object
    offset.top += el.outerHeight()/2; // middle of object
    minDist = Math.sqrt((offset.left - x)*(offset.left - x) + (offset.top - y)*(offset.top - y));

    for (var i=0; i < elements.length; i++) {
        el = elements.eq(i);
        offset = el.offset();
        offset.left += el.outerWidth()/2; // center of object
        offset.top += el.outerHeight()/2; // middle of object
        dist = Math.sqrt((offset.left - x)*(offset.left - x) + (offset.top - y)*(offset.top - y));
        if (dist < minDist) {
            minDist = dist;
            closestEl = el;
        }
    }

    return closestEl;
Felix
+2  A: 

I've built a jQuery method that returns closest element to offset, within the collection:

closestToOffset: function(offset) {
    var el = null, elOffset, x = offset.left, y = offset.top, distance, dx, dy, minDistance;
    this.each(function() {
        elOffset = $(this).offset();

        if (
        (x >= elOffset.left)  && (x <= elOffset.right) &&
        (y >= elOffset.top)   && (y <= elOffset.bottom)
        ) {
            el = $(this);
            return false;
        }

        var offsets = [[elOffset.left, elOffset.top], [elOffset.right, elOffset.top], [elOffset.left, elOffset.bottom], [elOffset.right, elOffset.bottom]];
        for (off in offsets) {
            dx = offsets[off][0] - x;
            dy = offsets[off][1] - y;
            distance = Math.sqrt((dx*dx) + (dy*dy));
            if (minDistance === undefined || distance < minDistance) {
                minDistance = distance;
                el = $(this);
            }
        }
    });
    return el;
}

Few notes:

  1. If the offset is inside one of the elements, it will be returned.
  2. I'm looping through four offsets, because this gives the best accuracy.

Use it like this:

$('div.myCollection').closestToOffset({left: 5, top: 5});
Sagi