By monaung


2009-01-14 09:35:03 8 Comments

I want to know how to get the X and Y position of HTML elements such as img and div in JavaScript.

26 comments

@Mustkeem K 2018-09-24 10:41:35

If you want it done only in javascript, here are some one liners using getBoundingClientRect()

window.scrollX + document.querySelector('#elementId').getBoundingClientRect().left // X

window.scrollY + document.querySelector('#elementId').getBoundingClientRect().top // Y

The first line will return offsetLeft say X relative to document.

The second line will return offsetTop say Y relative to document.

getBoundingClientRect() is a javascript function that returns the position of the element relative to viewport of window.

@E Alexis MT 2019-09-21 01:07:16

This should be the solution. There is no long code bs here, its understandable and its very accurate!

@Dmitri 2019-11-08 11:11:13

what if the element is within a scrollable element? The only way to do it is to sum offsetTop of all parent elements, like other answers suggest

@Văn Quyết 2019-08-29 04:10:50

/**
 *
 * @param {HTMLElement} el
 * @return {{top: number, left: number}}
 */
function getDocumentOffsetPosition(el) {
    var position = {
        top: el.offsetTop,
        left: el.offsetLeft
    };
    if (el.offsetParent) {
        var parentPosition = getDocumentOffsetPosition(el.offsetParent);
        position.top += parentPosition.top;
        position.left += parentPosition.left;
    }
    return position;
}

Thank ThinkingStiff for the answer, this is only another version of it.

@Kevin K. 2019-03-01 13:56:36

To get the total offset of an element, you could recursively sum up all parent offsets:

function getParentOffsets(el): number {
if (el.offsetParent) {
    return el.offsetParent.offsetTop + getParentOffset(el.offsetParent);
} else {
    return 0;
}
}

with this utility function the total top offset of a dom element is:

el.offsetTop + getParentOffsets(el);

@Adam Grant 2015-01-29 18:46:34

This worked for me (modified from highest voted answer):

function getOffset(el) {
  const rect = el.getBoundingClientRect();
  return {
    left: rect.left + window.scrollX,
    top: rect.top + window.scrollY
  };
}

Using this we can call

getOffset(element).left

or

getOffset(element).top

@nelek 2015-09-03 20:57:42

Only this solution working for me when element is placed in div whose centered using css top:50%, left:50%; transform:translate(-50%, -50%); ... excellent. Agree whit @ScottBiggs question. Logical is to seek for simplicity.

@niltoid 2016-06-10 21:36:20

maybe its not a winner cause its the same as Andy E's solution, but doesn't work in old browsers < IE9

@frumbert 2017-11-22 04:14:52

getBoundingClientRect causes a massive repaint spike in my performance charts

@arrowman 2018-01-30 11:33:30

Shouldn't it be ";" after return {...} ?

@tokland 2018-06-25 08:41:49

As a general rule, it's not advisable to reuse variables, but here it's really weird to reuse el, why not var rect = ... or something? it's much more clear.

@Adam Grant 2018-07-17 16:25:54

@tokland I don't think that's a "general rule" although it is a feature of some programming styles like functional programming. That being said, I can see how it would optimize the above. Can you send a gist? Happy to consider an edit to improve this further. Thank you.

@tokland 2018-07-17 19:21:49

@AdamGrant, I'd just change rename the second el, for example to rect.

@hev1 2018-08-08 01:44:13

You should define rect using var, let, or const. Otherwise, it is defined as window.rect.

@Adam Grant 2018-08-09 18:13:06

@hev1 Also could use this But const makes sense. Just changed. Thanks!

@hev1 2018-08-09 21:31:45

@AdamGrant No problem.

@abhishake 2019-04-05 10:35:45

left: rect.left + window.scrollX + (rect.width / 2), top: rect.top + window.scrollY + (rect.height / 2) will return the mid position of an element

@Adam Grant 2019-04-10 21:34:28

Neat! @abhishake

@Refik Ayata 2010-08-12 20:14:33

If page includes - at least- any "DIV", the function given by meouw throws the "Y" value beyond current page limits. In order to find the exact position, you need to handle both offsetParent's and parentNode's.

Try the code given below (it is checked for FF2):


var getAbsPosition = function(el){
    var el2 = el;
    var curtop = 0;
    var curleft = 0;
    if (document.getElementById || document.all) {
        do  {
            curleft += el.offsetLeft-el.scrollLeft;
            curtop += el.offsetTop-el.scrollTop;
            el = el.offsetParent;
            el2 = el2.parentNode;
            while (el2 != el) {
                curleft -= el2.scrollLeft;
                curtop -= el2.scrollTop;
                el2 = el2.parentNode;
            }
        } while (el.offsetParent);

    } else if (document.layers) {
        curtop += el.y;
        curleft += el.x;
    }
    return [curtop, curleft];
};

@rob 2014-05-12 20:46:42

as with the other solutions even with FF 24.4 the above code does not work when border widths exist as part of the positioning layout.

@Will Byrne 2015-06-10 21:27:03

You're a life saver. I'm working through some pretty deep GWT bugs right now and throwing this in a JSNI method solves all my problems!!

@MER 2017-10-24 19:23:22

While this is very likely to be lost at the bottom of so many answers, the top solutions here were not working for me.
As far as I could tell neither would any of the other answers have helped.

Situation:
In an HTML5 page I had a menu that was a nav element inside a header (not THE header but a header in another element).
I wanted the navigation to stick to the top once a user scrolled to it, but previous to this the header was absolute positioned (so I could have it overlay something else slightly).
The solutions above never triggered a change because .offsetTop was not going to change as this was an absolute positioned element. Additionally the .scrollTop property was simply the top of the top most element... that is to say 0 and always would be 0.
Any tests I performed utilizing these two (and same with getBoundingClientRect results) would not tell me if the top of the navigation bar ever scrolled to the top of the viewable page (again, as reported in console, they simply stayed the same numbers while scrolling occurred).

Solution
The solution for me was utilizing

window.visualViewport.pageTop

The value of the pageTop property reflects the viewable section of the screen, therefore allowing me to track where an element is in reference to the boundaries of the viewable area.

Probably unnecessary to say, anytime I am dealing with scrolling I expect to use this solution to programatically respond to movement of elements being scrolled.
Hope it helps someone else.
IMPORTANT NOTE: This appears to work in Chrome and Opera currently & definitely not in Firefox (6-2018)... until Firefox supports visualViewport I recommend NOT using this method, (and I hope they do soon... it makes a lot more sense than the rest).


UPDATE:
Just a note regarding this solution.
While I still find what I discovered to be very valuable for situations in which "...programmatically respond to movement of elements being scrolled." is applicable. The better solution for the problem that I had was to use CSS to set position: sticky on the element. Using sticky you can have an element stay at the top without using javascript (NOTE: there are times this will not work as effectively as changing the element to fixed but for most uses the sticky approach will likely be superior)

UPDATE01:
So I realized that for a different page I had a requirement where I needed to detect the position of an element in a mildly complex scrolling setup (parallax plus elements that scroll past as part of a message). I realized in that scenario that the following provided the value I utilized to determine when to do something:

  let bodyElement = document.getElementsByTagName('body')[0];
  let elementToTrack = bodyElement.querySelector('.trackme');
  trackedObjPos = elementToTrack.getBoundingClientRect().top;
  if(trackedObjPos > 264)
  {
    bodyElement.style.cssText = '';
  }

Hope this answer is more widely useful now.

@lucchi 2018-06-01 11:48:42

this is very likely to be at the top of so many answers when you click active to sort the answers

@MER 2018-06-01 20:14:41

@lucchi :D well I suppose I could remove the text @ the top... though I just happened on this one again and realized that I'd found a better solution for my own problem... though my answer is more of a foot note to the more complete answers. I suspect my requirements were maybe not that common that the other answers didn't work for me?

@lucchi 2018-06-01 22:22:56

I appreciate your answer anyway, it was just to tell you that, anybody willing will be aware that you wrote something new. You are not alone....

@Vishal 2018-04-09 19:50:09

Get position of div in respect to left and Top

  var elm = $('#div_id');  //get the div
  var posY_top = elm.offset().top;  //get the position from top
  var posX_left = elm.offset().left; //get the position from left 

@MacroMan 2018-05-08 14:39:01

I down voted because bottom and right are not properties of offset()

@Vishal 2018-05-08 16:56:00

@MacroMan, I respects your decision but I have already explained in the text that the "bottom and right the code is not tested.". Also, i will update my code very soon.

@mesqueeb 2018-07-10 06:57:44

I think it's el.offsetTop and el.offsetLeft (the OP doesn't say anything about jQuery... I'm not sure why your answer uses jQuery either...)

@Andy E 2012-07-09 14:06:24

The correct approach is to use element.getBoundingClientRect():

var rect = element.getBoundingClientRect();
console.log(rect.top, rect.right, rect.bottom, rect.left);

Internet Explorer has supported this since as long as you are likely to care about and it was finally standardized in CSSOM Views. All other browsers adopted it a long time ago.

Some browsers also return height and width properties, though this is non-standard. If you're worried about older browser compatibility, check this answer's revisions for an optimised degrading implementation.

The values returned by element.getBoundingClientRect() are relative to the viewport. If you need it relative to another element, simply subtract one rectangle from the other:

var bodyRect = document.body.getBoundingClientRect(),
    elemRect = element.getBoundingClientRect(),
    offset   = elemRect.top - bodyRect.top;

alert('Element is ' + offset + ' vertical pixels from <body>');

@Ring Ø 2014-02-19 10:55:52

A very interesting article on JS coordinates. This article is mentioned nowhere on SO, it should..

@CpnCrunch 2014-07-01 02:30:23

Doesn't work...it's 8 pixels out vertically and horizontally, due to the 8px margin in the body. Meouw's solution works perfectly. If you want I have a small test to demonstrate the problem.

@Andy E 2014-07-02 08:43:06

@CpnCrunch: which browser are you seeing that in? In my testing, meouw's code and mine return the same result no matter what the body element's margin is.

@CpnCrunch 2014-07-03 17:49:34

Breaks in both FF and chrome. See jsfiddle.net/D9VVy for the bug (should draw the two boxes inside each other). For working version (using meouw's code) see jsfiddle.net/Tn4FS

@CpnCrunch 2014-07-03 17:58:22

Actually, I think it depends what you actually want to get the coords of. The question is a little ambiguous. Your answer is correct if you just want the coords relative to the viewport or relative to the body element, but that doesn't help you in the case where you want the absolute position of the element (which I imagine is what most people want). meouw's answer doesn't give you that either, unless you remove the '-scrollLeft' and '-scrollTop' bits.

@Andy E 2014-07-04 15:14:10

@CpnCrunch: I see what you mean now; I didn't realise you were referring to the latter part of my answer. When the body has a margin, its bounding box will be offset by that number of pixels at each respective side. In that case, you'd also have to subtract the computed margin style from the body coordinates. It's worth noting that CSS resets remove the margin from <body>, though.

@Timo Kähkönen 2015-05-31 17:12:53

This answers solution elemRect.top - bodyRect.top and similarly elemRect.left - bodyRect.left is fortunately zoom-tolerant. jQuery's offset() gives different results when the window is zoomed in Android Chrome. jQuery surrenders regarding offset(): dimensions may be incorrect when the page is zoomed by the user; browsers do not expose an API to detect this condition. Using this answers solution you don't need an API to detect zoom condition.

@Dan Dascalescu 2015-07-21 07:51:08

@Andy E 2015-07-21 08:45:14

@DanDascalescu see the second part of my answer, where an element's position is compared to the body element's position.

@Finbar Maginn 2015-12-07 14:09:17

How would I get the distance to the very top of the page?

@yckart 2016-05-22 11:41:13

document.scrollingElement would be so helpful here...!

@ceving 2017-12-10 12:33:13

@RingØ +1 for pageXOffset and pageYOffset

@sompylasar 2018-02-07 21:48:54

@RingØ – great article but the "Getting document coordinates" part of it that only uses pageXOffset, pageYOffset is not universal as it does not account for nested scrollable regions.

@Suraj Jain 2018-04-12 03:06:02

@RingØ javascript.info is cool site, it has lots of good information.

@Jānis Elmeris 2018-05-02 17:58:30

element.getBoundingClientRect().top does not return correct top offset in case of a position: fixed element in iOS (iPhone 8) if it contains a focused input element and the virtual keyboard has slid up. For example, if the actual offset from the window's top is 200px, it would report it as 420px, where 220px is the height of the virtual keyboard. If you set element's position: absolute and position it at the same position as if fixed, then you'll get the correct 200px. I don't know a solution to this.

@theVoogie 2019-03-11 13:31:21

Wow, this is simply from another planet. Thanks!

@Wayne Wei 2019-09-10 07:29:35

This is a classic solution.

@Alireza 2017-05-22 13:09:51

How about something like this, by passing ID of the element and it will return the left or top, we can also combine them:

1) find left

function findLeft(element) {
  var rec = document.getElementById(element).getBoundingClientRect();
  return rec.left + window.scrollX;
} //call it like findLeft('#header');

2) find top

function findTop(element) {
  var rec = document.getElementById(element).getBoundingClientRect();
  return rec.top + window.scrollY;
} //call it like findTop('#header');

or 3) find left and top together

function findTopLeft(element) {
  var rec = document.getElementById(element).getBoundingClientRect();
  return {top: rec.top + window.scrollY, left: rec.left + window.scrollX};
} //call it like findTopLeft('#header');

@akauppi 2010-08-18 14:43:35

jQuery .offset() will get the current coordinates of the first element, or set the coordinates of every element, in the set of matched elements, relative to the document.

@Abdul Rahim Haddad 2013-08-10 16:19:34

For some reason it is not giving the same results in IE compared to other browsers. I think that in IE it is giving position relative to the window, so if you scroll, you will be getting different results in IE compared to others

@Timo Kähkönen 2015-05-31 18:45:06

offset() is confused by zoom, at least in Android Chrome, so it is useless. stackoverflow.com/a/11396681/1691517 shows zoom-tolerant way.

@ThinkingStiff 2012-01-14 08:57:16

You can add two properties to Element.prototype to get the top/left of any element.

Object.defineProperty( Element.prototype, 'documentOffsetTop', {
    get: function () { 
        return this.offsetTop + ( this.offsetParent ? this.offsetParent.documentOffsetTop : 0 );
    }
} );

Object.defineProperty( Element.prototype, 'documentOffsetLeft', {
    get: function () { 
        return this.offsetLeft + ( this.offsetParent ? this.offsetParent.documentOffsetLeft : 0 );
    }
} );

This is called like this:

var x = document.getElementById( 'myDiv' ).documentOffsetLeft;

Here's a demo comparing the results to jQuery's offset().top and .left: http://jsfiddle.net/ThinkingStiff/3G7EZ/

@Jared Forsyth 2015-06-22 21:58:30

modifying Element.prototype is generally considered a bad idea. It leads to really hard to maintain code. Also, this code doesn't account for scrolling.

@KingRider 2016-08-25 15:44:24

Difference between small and little

function getPosition( el ) {
    var x = 0;
    var y = 0;
    while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
    x += el.offsetLeft - el.scrollLeft;
    y += el.offsetTop - el.scrollTop;
    el = el.offsetParent;
    }
    return { top: y, left: x };
}

Look a example coordinates: http://javascript.info/tutorial/coordinates

@Jonathan Eunice 2016-10-19 09:39:29

This unfortunately does not work for all elements one might find inside a modern HTML document. Embedded <svg> elements, for example, do not have offsetLeft, offsetTop, and offsetParent properties.

@KingRider 2016-10-19 20:24:17

Its just DIV or IMG, not SVG. View "Look a example coordinates"? you can find object svg is position call getOffsetRect, try and depend object work.

@James Carlyle-Clarke 2015-12-01 07:24:49

I've taken @meouw's answer, added in the clientLeft that allows for the border, and then created three versions:

getAbsoluteOffsetFromBody - similar to @meouw's, this gets the absolute position relative to the body or html element of the document (depending on quirks mode)

getAbsoluteOffsetFromGivenElement - returns the absolute position relative to the given element (relativeEl). Note that the given element must contain the element el, or this will behave the same as getAbsoluteOffsetFromBody. This is useful if you have two elements contained within another (known) element (optionally several nodes up the node tree) and want to make them the same position.

getAbsoluteOffsetFromRelative - returns the absolute position relative to the first parent element with position: relative. This is similar to getAbsoluteOffsetFromGivenElement, for the same reason but will only go as far as the first matching element.

getAbsoluteOffsetFromBody = function( el )
{   // finds the offset of el from the body or html element
    var _x = 0;
    var _y = 0;
    while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) )
    {
        _x += el.offsetLeft - el.scrollLeft + el.clientLeft;
        _y += el.offsetTop - el.scrollTop + el.clientTop;
        el = el.offsetParent;
    }
    return { top: _y, left: _x };
}

getAbsoluteOffsetFromGivenElement = function( el, relativeEl )
{   // finds the offset of el from relativeEl
    var _x = 0;
    var _y = 0;
    while( el && el != relativeEl && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) )
    {
        _x += el.offsetLeft - el.scrollLeft + el.clientLeft;
        _y += el.offsetTop - el.scrollTop + el.clientTop;
        el = el.offsetParent;
    }
    return { top: _y, left: _x };
}

getAbsoluteOffsetFromRelative = function( el )
{   // finds the offset of el from the first parent with position: relative
    var _x = 0;
    var _y = 0;
    while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) )
    {
        _x += el.offsetLeft - el.scrollLeft + el.clientLeft;
        _y += el.offsetTop - el.scrollTop + el.clientTop;
        el = el.offsetParent;
        if (el != null)
        {
            if (getComputedStyle !== 'undefined')
                valString = getComputedStyle(el, null).getPropertyValue('position');
            else
                valString = el.currentStyle['position'];
            if (valString === "relative")
                el = null;
        }
    }
    return { top: _y, left: _x };
}

If you are still having problems, particularly relating to scrolling, you could try looking at http://www.greywyvern.com/?post=331 - I noticed at least one piece of questionable code in getStyle which should be fine assuming browsers behave, but haven't tested the rest at all.

@Norman 2018-01-13 16:13:54

Interesting ... but on Edge getAbsoluteOffsetFromBody(element).top returns a different value as I scroll, in Chrome it stays consistent as I scroll. It seems Edge is adjusting with scroll position. When the element is scrolled out of view I get a negative value.

@Nolyurn 2018-08-13 15:52:58

getAbsoluteOffsetFromRelative made my day, i had to reproduce bootstrap tooltip without Javascript of bootstrap, it helped me a lot.

@meouw 2009-01-14 10:04:45

The libraries go to some lengths to get accurate offsets for an element.
here's a simple function that does the job in every circumstances that I've tried.

function getOffset( el ) {
    var _x = 0;
    var _y = 0;
    while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
        _x += el.offsetLeft - el.scrollLeft;
        _y += el.offsetTop - el.scrollTop;
        el = el.offsetParent;
    }
    return { top: _y, left: _x };
}
var x = getOffset( document.getElementById('yourElId') ).left; 

@Adam 2009-07-29 20:11:45

Doesn't seem to work with iframes nested in each other.

@Adam 2009-07-29 20:19:03

change: el = el.parentNode to: el = el.offsetParent; and it seems to work for nested iframes now... I'm thinking that's what you intended?

@ck_ 2011-08-05 05:21:30

This solution is incomplete. Try putting a thick border on a div and nest it a few times. In any version of Firefox (2-5) it will be off by the border width(s) (even in standards compliance mode).

@Azmisov 2012-04-29 05:15:23

You may also need to do _y+=Math.abs(document.body.offsetTop) and _x+=Math.abs(document.body.offsetLeft) for Firefox.

@baptx 2012-05-30 17:04:37

isn't there a cleaner way to do that, like a el.totalOffsetLeft and totalOffsetTop function? jQuery certainly has this option but it's a shame there is no official method for this, do we need to wait DOM4? I'm building an HTML5 canvas application, so I think the best solution would be to insert canvas element in an iframe so I can get real coordinates with clientX and clientY when we click on element.

@Arjan 2012-08-11 23:35:34

So, @Adam and meouw, are you saying the first code block is wrong? Then why not remove that altogether? (This question has seen quite some viewers over the years; might be nice to remove things if they are wrong?)

@Arjan 2012-08-11 23:42:37

(As an aside: the code blocks are discussed at What to do when someone adds an alternative code with just a minimal modification?, @Adam and meouw.)

@Andy E 2013-02-08 18:49:53

@baptx: I know this is a late reply, but I didn't see your comment sooner. See my answer below for a more standards compliant alternative ― you don't even need the fallback code really, since browsers have supported it for a long time.

@Costa 2013-03-27 06:16:02

Hey, thanks for the answer! I'm still getting inaccurate results. Here's my code: stackoverflow.com/questions/15648464/…

@Alex 2014-08-26 21:44:14

This doesn't work for transformed elements.

@Dan Dascalescu 2015-07-21 12:51:29

No need to do this for IE4+ (four). See Andy's answer.

@Nikos 2015-08-13 14:44:49

Offset doesn't work accurately in some browsers when you zoom, can you advise?

@mrtnmgs 2019-09-11 15:09:59

Also won't work for svg elements (<svg>, <g>, path> etc)

@Duncan 2014-10-12 10:57:36

Just thought I'd throw this out there as well.
I haven't been able to test it in older browsers, but it works in the latest of the top 3. :)

Element.prototype.getOffsetTop = function() {
    return ( this.parentElement )? this.offsetTop + this.parentElement.getOffsetTop(): this.offsetTop;
};
Element.prototype.getOffsetLeft = function() {
    return ( this.parentElement )? this.offsetLeft + this.parentElement.getOffsetLeft(): this.offsetLeft;
};
Element.prototype.getOffset = function() {
    return {'left':this.getOffsetLeft(),'top':this.getOffsetTop()};
};

@kernowcode 2015-06-17 20:48:24

After much research and testing this seems to work

function getPosition(e) {
    var isNotFirefox = (navigator.userAgent.toLowerCase().indexOf('firefox') == -1);
    var x = 0, y = 0;
    while (e) {
        x += e.offsetLeft - e.scrollLeft + (isNotFirefox ? e.clientLeft : 0);
        y += e.offsetTop - e.scrollTop + (isNotFirefox ? e.clientTop : 0);
        e = e.offsetParent;
    }
    return { x: x + window.scrollX, y: y + window.scrollY };
}

see http://jsbin.com/xuvovalifo/edit?html,js,output

@Bojan Kseneman 2015-03-15 12:11:11

I did it like this so it was cross-compatible with old browsers.

// For really old browser's or incompatible ones
    function getOffsetSum(elem) {
        var top = 0,
            left = 0,
            bottom = 0,
            right = 0

         var width = elem.offsetWidth;
         var height = elem.offsetHeight;

        while (elem) {
            top += elem.offsetTop;
            left += elem.offsetLeft;
            elem = elem.offsetParent;
        }

         right = left + width;
         bottom = top + height;

        return {
            top: top,
            left: left,
            bottom: bottom,
            right: right,
        }
    }

    function getOffsetRect(elem) {
        var box = elem.getBoundingClientRect();

        var body = document.body;
        var docElem = document.documentElement;

        var scrollTop = window.pageYOffset || docElem.scrollTop || body.scrollTop;
        var scrollLeft = window.pageXOffset || docElem.scrollLeft || body.scrollLeft;

        var clientTop = docElem.clientTop;
        var clientLeft = docElem.clientLeft;


        var top = box.top + scrollTop - clientTop;
        var left = box.left + scrollLeft - clientLeft;
        var bottom = top + (box.bottom - box.top);
        var right = left + (box.right - box.left);

        return {
            top: Math.round(top),
            left: Math.round(left),
            bottom: Math.round(bottom),
            right: Math.round(right),
        }
    }

    function getOffset(elem) {
        if (elem) {
            if (elem.getBoundingClientRect) {
                return getOffsetRect(elem);
            } else { // old browser
                return getOffsetSum(elem);
            }
        } else
            return null;
    }

More about coordinates in JavaScript here: http://javascript.info/tutorial/coordinates

@samadadi 2015-01-08 17:42:16

Since different browsers are rendering border, padding, margin and etc in different way. I wrote a little function to retrieve top and left positions of specific element in every root element that you want in precise dimension:

function getTop(root, offset) {
    var rootRect = root.getBoundingClientRect();
    var offsetRect = offset.getBoundingClientRect();
    return offsetRect.top - rootRect.top;
}

For retrieve left position you must return:

    return offsetRect.left - rootRect.left;

@James Montagne 2014-11-04 14:17:15

The cleanest approach I have found is a simplified version of the technique used by jQuery's offset. Similar to some of the other answers it starts with getBoundingClientRect; it then uses the window and the documentElement to adjust for scroll position as well as things like the margin on the body (often the default).

var rect = el.getBoundingClientRect();
var docEl = document.documentElement;

var rectTop = rect.top + window.pageYOffset - docEl.clientTop;
var rectLeft = rect.left + window.pageXOffset - docEl.clientLeft;

var els = document.getElementsByTagName("div");
var docEl = document.documentElement;

for (var i = 0; i < els.length; i++) {

  var rect = els[i].getBoundingClientRect();

  var rectTop = rect.top + window.pageYOffset - docEl.clientTop;
  var rectLeft = rect.left + window.pageXOffset - docEl.clientLeft;

  els[i].innerHTML = "<b>" + rectLeft + ", " + rectTop + "</b>";
}
div {
  width: 100px;
  height: 100px;
  background-color: red;
  border: 1px solid black;
}
#rel {
  position: relative;
  left: 10px;
  top: 10px;
}
#abs {
  position: absolute;
  top: 250px;
  left: 250px;
}
<div id="rel"></div>
<div id="abs"></div>
<div></div>

@Mark Espinoza 2014-10-23 15:02:06

I successfully used Andy E's solution to position a bootstrap 2 modal depending on what link in a table row a user clicks on. The page is a Tapestry 5 page and javascript below is imported in the java page class.

javascript:

function setLinkPosition(clientId){
var bodyRect = document.body.getBoundingClientRect(),
elemRect = clientId.getBoundingClientRect(),
offset   = elemRect.top - bodyRect.top;
offset   = offset + 20;
$('#serviceLineModal').css("top", offset);

}

My modal code:

<div id="serviceLineModal" class="modal hide fade add-absolute-position" data-backdrop="static" 
 tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" style="top:50%;">
<div class="modal-header">
    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
    <h3 id="myModalLabel">Modal header</h3>
</div>

<div class="modal-body">
    <t:zone t:id="modalZone" id="modalZone">
        <p>You selected service line number: ${serviceLineNumberSelected}</p>
    </t:zone>
</div>

<div class="modal-footer">
    <button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
    <!-- <button class="btn btn-primary">Save changes</button> -->
</div>

The link in the loop:

<t:loop source="servicesToDisplay" value="service" encoder="encoder">
<tr style="border-right: 1px solid black;">       
    <td style="white-space:nowrap;" class="add-padding-left-and-right no-border"> 
        <a t:type="eventLink" t:event="serviceLineNumberSelected" t:context="service.serviceLineNumber" 
            t:zone="pageZone" t:clientId="modalLink${service.serviceLineNumber}"
            onmouseover="setLinkPosition(this);">
            <i class="icon-chevron-down"></i> <!-- ${service.serviceLineNumber} -->
        </a>
    </td>

And the java code in the page class:

void onServiceLineNumberSelected(String number){
    checkForNullSession();
    serviceLineNumberSelected = number;
    addOpenServiceLineDialogCommand();
    ajaxResponseRenderer.addRender(modalZone);
}

protected void addOpenServiceLineDialogCommand() {
    ajaxResponseRenderer.addCallback(new JavaScriptCallback() {
        @Override
        public void run(JavaScriptSupport javascriptSupport) {
            javascriptSupport.addScript("$('#serviceLineModal').modal('show');");
        }
    });
}

Hope this helps someone, this post helped out.

@Brad Martin 2015-08-14 21:32:45

This js code helped me, I have an old webforms app using multiple placeholders inject .aspx pages and I could not figure out how to appendChild() of a popup box I was creating to get it into the viewport because the entire DOM tree is whack with the multiple placeholders and incorrect markup. Using the body.getBoundingClientRect() then using its top in positioning was what I needed. Thanks.

@Abdul Rahim Haddad 2013-07-26 01:49:09

To retrieve the position relative to the page efficiently, and without using a recursive function: (includes IE also)

var element = document.getElementById('elementId'); //replace elementId with your element's Id.
var rect = element.getBoundingClientRect();
var elementLeft,elementTop; //x and y
var scrollTop = document.documentElement.scrollTop?
                document.documentElement.scrollTop:document.body.scrollTop;
var scrollLeft = document.documentElement.scrollLeft?                   
                 document.documentElement.scrollLeft:document.body.scrollLeft;
elementTop = rect.top+scrollTop;
elementLeft = rect.left+scrollLeft;

@scunliffe 2014-12-06 22:24:38

Including the all important scrollTop/scrollLeft makes this answer a bit more correct.

@Adam Boostani 2013-04-16 04:56:33

If you are using jQuery, this could be a simple solution:

<script>
  var el = $("#element");
  var position = el.position();
  console.log( "left: " + position.left + ", top: " + position.top );
</script>

@Ron Reiter 2011-09-10 17:05:08

This is the best code I've managed to create (works in iframes as well, unlike jQuery's offset()). Seems webkit has a bit of a different behavior.

Based on meouw's comment:

function getOffset( el ) {
    var _x = 0;
    var _y = 0;
    while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
        _x += el.offsetLeft - el.scrollLeft;
        _y += el.offsetTop - el.scrollTop;
        // chrome/safari
        if ($.browser.webkit) {
            el = el.parentNode;
        } else {
            // firefox/IE
            el = el.offsetParent;
        }
    }
    return { top: _y, left: _x };
}

@Sathvik 2012-03-07 02:47:29

Note that you'll need jQuery to use $.browser.webkit; You'll need to play around with navigator.userAgent to do the same with pure JavaScript.

@Gus 2014-01-12 18:22:38

$.browser is no longer available in the latest version of jQuery

@rob 2014-05-12 20:43:46

unfortunately even with FF 24.4 the above code does not work when border widths exist as part of the positioning layout.

@John_ 2009-01-14 10:24:21

if using jQuery, the dimensions plugin is excellent and allows you specify exactly what you want.

e.g.

Relative position, absolute position, absolute position without padding, with padding...

It goes on, let's just say there is a lot you can do with it.

Plus the bonus of using jQuery is it's lightweight file size and easy use, you won't go back to JavaScript without it afterwards.

@Shalom Craimer 2009-01-14 09:47:21

You might be better served by using a JavaScript framework, that has functions to return such information (and so much more!) in a browser-independant fashion. Here are a few:

With these frameworks, you could do something like: $('id-of-img').top to get the y-pixel coordinate of the image.

@Costa 2013-03-27 06:16:45

Do you know how the implementations differ? Are the libraries using functions much like the onces listed above?

@Shalom Craimer 2013-03-27 17:38:08

@Costa They all use those sorts of functions, although this is an evolving field, as different browsers conform differently to the specification. So much of the code deals with "fixing" things that go wrong. You really should look at the source code.

@Costa 2013-03-27 17:49:31

I've been trying to do just that, and found this very useful: james.padolsey.com/jquery I've had a strange problem with library source code, any time I try to format it with a beautifier or text editor plugin, I still get javascript code on two lines.

@Sk8erPeter 2013-05-09 11:34:50

@scraimer: jQuery and YUI are NOT frameworks!!! These are libraries! It's not the same. Citing jquery.com: "jQuery is a fast, small, and feature-rich JavaScript library." Citing yuilibrary.com (you can see the "magic" word in the URL too...): "YUI is a free, open source JavaScript and CSS library for building richly interactive web applications."

@Opptatt Jobber 2013-10-11 19:14:50

So you should use huge libraries just for this simple task? Not necessary. I hate that every time I want to do something with js, I get these jQuery solutions. jQuery is not JS!

@Nick Manning 2014-03-27 00:31:35

@OpptattJobber I agree...JQuery is not javascript. It relies on Javascript but its a different programming language altogether.

@ginman 2014-04-04 15:01:14

@NickManning Its not a new language, its an abstraction layer for the DOM that eliminates having to write compatibility and performance workarounds directly into your code. If you don't use jQuery, you should be using some type of abstraction layer anyways.

@Karl 2015-05-22 18:41:13

jQuery is written in javascript so how could it be a different language all together.... come on sheeple.

@Iharob Al Asimi 2015-09-03 21:15:42

While this is a great suggestion it's not a direct answer to the question, reserve this for comments. And congratulations on knowing so many frameworks, with just JQuery it's confusing enough to learn more frameworks, because frameworks have a huge disadvantage, the programmer who writes the framework doesn't think like you do and hence it's very difficult to follow their logic. Unlike libraries, that just expose some kind of API that allows clear things to be done, and still sometimes in c libraries for instance, you don't know whether to free() something or not!

@Adam Whateverson 2018-08-23 13:20:10

I have to add an additional comment to this to clarify the whole "framework" vs "library" thing. A framework is something built to act as a layer around or on top of something else. A library is a collection of something. In my opinion, jQuery (+similar) are "frameworks" that rely on Javascript to create a "library" of tools to make tasks quicker. You don't have to learn how to do do them using raw Javascript yourself or worry about browser compatibility. You grow to learn and rely on their methods & custom Javascript functions. Great to speed up big tasks but can bloat smaller ones.

@AnthonyWJones 2009-01-14 09:43:14

HTML elements on most browsers will have:-

offsetLeft
offsetTop

These specifiy the position of the element relative its nearest parent that has layout. This parent can often be accessed bif the offsetParent property.

IE and FF3 have

clientLeft
clientTop

These properties are less common, they specify an elements position with its parents client area (padded area is part of the client area but border and margin is not).

Related Questions

Sponsored Content

88 Answered Questions

[SOLVED] How do I remove a particular element from an array in JavaScript?

  • 2011-04-23 22:17:18
  • Walker
  • 6152696 View
  • 7678 Score
  • 88 Answer
  • Tags:   javascript arrays

38 Answered Questions

[SOLVED] How do you get a timestamp in JavaScript?

9 Answered Questions

[SOLVED] Why does HTML think “chucknorris” is a color?

38 Answered Questions

[SOLVED] How do I loop through or enumerate a JavaScript object?

30 Answered Questions

[SOLVED] How to change an element's class with JavaScript?

  • 2008-10-12 20:06:43
  • Nathan Smith
  • 2483095 View
  • 2656 Score
  • 30 Answer
  • Tags:   javascript html dom

55 Answered Questions

[SOLVED] How do I check if an element is hidden in jQuery?

14 Answered Questions

[SOLVED] How to move an element into another element?

  • 2009-08-14 20:14:45
  • Mark Richman
  • 1054824 View
  • 1618 Score
  • 14 Answer
  • Tags:   javascript jquery html

31 Answered Questions

[SOLVED] Is there a CSS parent selector?

  • 2009-06-18 19:59:36
  • jcuenod
  • 1857064 View
  • 2991 Score
  • 31 Answer
  • Tags:   css css-selectors

34 Answered Questions

Sponsored Content