15

I've seen dozens of scripts that can catch the x and y position of an element/object within the page. But I am always having trouble with catching the x and y when the webpage is using margins at the body, or other elements, absolute/relative elements, such like that.

Is there a solution which provides the exact position, no matter what margins or paddings are used?

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
Johnny
  • 151
  • 1
  • 1
  • 3

4 Answers4

12

I use following code to move div box to follow cursor in this Web IME site

function xy(x) {
    o = document.getElementById(x);
    var l =o.offsetLeft; var t = o.offsetTop;
    while (o=o.offsetParent)
        l += o.offsetLeft;
    o = document.getElementById(x);
    while (o=o.offsetParent)
        t += o.offsetTop;
    return [l,t];
}

Its return an array [left,top],

YOU
  • 120,166
  • 34
  • 186
  • 219
  • 1
    Hi Mark, thank you for your code, but when the page have a margin at the body, the position isnt corrent anymore, you got a solution for that? Thanks in advance – Johnny Nov 20 '09 at 12:22
  • 1
    how about adding `document.body.offsetLeft` and `document.body.offsetTop` to the return values? – YOU Nov 20 '09 at 13:03
  • 2
    This should work fine regardless of body margins. Make sure you're using a Standards Mode doctype though. In Quirks all bets are off. – bobince Nov 20 '09 at 13:25
  • Thank you Mark, i will try that one! @Bobince, i do not have any control over the website where this code will be published, this is part of an interactive advertisement. – Johnny Nov 20 '09 at 13:44
  • 12
    Why do you traverse up the DOM tree twice if you can get both `offsetLeft` and `offsetTop` in one loop? – fent Feb 25 '12 at 18:55
  • @DeaDEnD, exactly... use the solution below instead. – Pacerier Aug 08 '14 at 19:41
  • ...In addition to looping twice for no reason...this also creates global ghost variable `o`, would fail in strict mode, and for some reason feels the need to call `document.getElementById` twice on the same id to set it to the same variable only a couple of lines apart... _this answer is downright terrible code, please use Pacerier's answer below._ – Jimbo Jonny Feb 15 '16 at 19:03
11

Getting the exact position is simply a matter of adding the offsetLefts and offsetTops recursively to the offsetParents:

function getPos(ele){
    var x=0;
    var y=0;
    while(true){
        x += ele.offsetLeft;
        y += ele.offsetTop;
        if(ele.offsetParent === null){
            break;
        }
        ele = ele.offsetParent;
    }
    return [x, y];
}

Btw, this solution would probably run twice faster than the other solution above since we only loop once.

Community
  • 1
  • 1
Pacerier
  • 86,231
  • 106
  • 366
  • 634
  • @YOUs code could be drastically simplified making them roughly the same, whereas his would be more concise. – Seiyria Aug 08 '14 at 16:49
8

offsetParent and other offset functions are old... use the getBoundingClientRect function... use this:

function getAbsPosition(element) {
   var rect = element.getBoundingClientRect();
   return {x:rect.left,y:rect.top}
}

now you can use it like this

<div style="margin:50px;padding:50px;" id="myEl">lol</div>
<script type="text/javascript">
    var coords = getAbsPosition( document.getElementById('myEl') );
    alert( coords.x );alert( coords.y );
</script>

Don't worry... no matter how much margin or position or padding the element has, it always works

jrbedard
  • 3,662
  • 5
  • 30
  • 34
Sayan J. Das
  • 852
  • 10
  • 19
0

Here are some improvements on @Pacerier 's code in my own answer:

function getPos(el, rel)
{
    var x=0, y=0;
    do {
        x += el.offsetLeft;
        y += el.offsetTop;
        el = el.offsetParent;
    }
    while (el != rel)
    return {x:x, y:y};
}

If is just used as getPos(myElem) will return global position. If a second element is included as an argument (i.e. getPos(myElem, someAncestor)) that is an ancestor/parent of the first (at least somewhere up the chain) then it will give the position relative to that ancestor. If rel is not given (i.e. is left undefined), then it purposefully uses != instead of !== because it should stop when el gets to null and rel is undefined as well, so the non-strict equality is purposeful, don't change it. It also returns an object, since that's a bit more readable in usage than an array (and so you can do something like getPos().x).

This is derived from Pacerier's solution, so if you're gonna upvote this, consider upvoting his too.

kontur
  • 4,934
  • 2
  • 36
  • 62
Jimbo Jonny
  • 3,549
  • 1
  • 19
  • 23