<< Update ListBox item in Windows Forms | Home | Item dragging in Windows Forms ListBox control >>
ASP.NET query builder for your web application

Absolute coordinates of DOM element within document

The problem

Sometimes (especially in AJAX projects) it is necessary to get the position of some DOM element in "absolute" coordinates within current document.
For example such "absolute" position is needed if you would like to show some hidden DIV object exactly on the position (or with some offset) of another element. We use this function in our EasyQuery.NET WebForms library to show popup menu under some condition element (you can see an example here).

The solution

Such properties as style.left, style.top or offsetLeft, offsetTop can be used to get (or set) the position of element within its parent. So to get absolute element's position within document we should move upward on element's tree and add the position of all element's parents (except the latest document element).

However it is not quite easy. There are still some problems:

  1. First, we need to take into account possible scrolling in element's parents and decrease our result accordingly.
  2. Second, there are some distinctions in behavior of different browsers (as usual :-( ). For Internet Explorer we always can just subtract scrolling position of the object stored in element's offsetParent prooperty. But for FireFox we also need to take into consideration all parents accessible by parentNode properties.
  3. Finally, we should take into account the border width for some parent elements. Unfortunately this task is not so easy as it can be supposed especially for Internet Explorer browser.

So here is the function we get in result:

function __getIEVersion() {
    var rv = -1; // Return value assumes failure.
    if (navigator.appName == 'Microsoft Internet Explorer') {
        var ua = navigator.userAgent;
        var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
        if (re.exec(ua) != null)
            rv = parseFloat(RegExp.$1);
    }
    return rv;
}

function __getOperaVersion() {
    var rv = 0; // Default value
    if (window.opera) {
        var sver = window.opera.version();
        rv = parseFloat(sver);
    }
    return rv;
}

var __userAgent = navigator.userAgent;
var __isIE =  navigator.appVersion.match(/MSIE/) != null;
var __IEVersion = __getIEVersion();
var __isIENew = __isIE && __IEVersion >= 8;
var __isIEOld = __isIE && !__isIENew;

var __isFireFox = __userAgent.match(/firefox/i) != null;
var __isFireFoxOld = __isFireFox && ((__userAgent.match(/firefox\/2./i) != null) || (__userAgent.match(/firefox\/1./i) != null));
var __isFireFoxNew = __isFireFox && !__isFireFoxOld;

var __isWebKit =  navigator.appVersion.match(/WebKit/) != null;
var __isChrome =  navigator.appVersion.match(/Chrome/) != null;
var __isOpera =  window.opera != null;
var __operaVersion = __getOperaVersion();
var __isOperaOld = __isOpera && (__operaVersion < 10);

function __parseBorderWidth(width) {
    var res = 0;
    if (typeof(width) == "string" && width != null && width != "" ) {
        var p = width.indexOf("px");
        if (p >= 0) {
            res = parseInt(width.substring(0, p));
        }
        else {
     		//do not know how to calculate other values (such as 0.5em or 0.1cm) correctly now
    		//so just set the width to 1 pixel
            res = 1; 
        }
    }
    return res;
}


//returns border width for some element
function __getBorderWidth(element) {
	var res = new Object();
	res.left = 0; res.top = 0; res.right = 0; res.bottom = 0;
	if (window.getComputedStyle) {
		//for Firefox
		var elStyle = window.getComputedStyle(element, null);
		res.left = parseInt(elStyle.borderLeftWidth.slice(0, -2));  
		res.top = parseInt(elStyle.borderTopWidth.slice(0, -2));  
		res.right = parseInt(elStyle.borderRightWidth.slice(0, -2));  
		res.bottom = parseInt(elStyle.borderBottomWidth.slice(0, -2));  
	}
	else {
		//for other browsers
		res.left = __parseBorderWidth(element.style.borderLeftWidth);
		res.top = __parseBorderWidth(element.style.borderTopWidth);
		res.right = __parseBorderWidth(element.style.borderRightWidth);
		res.bottom = __parseBorderWidth(element.style.borderBottomWidth);
	}
   
	return res;
}


//returns the absolute position of some element within document
function getElementAbsolutePos(elemID) {
	var element;
	if (typeof(elemID) == "string")	{
		element = document.getElementById(elemID);
	}
	else {
		element = elemID;
	}

	var res = new Object();
	res.x = 0; res.y = 0;
	if (element !== null) {
    	res.x = element.offsetLeft;

		var offsetParent = element.offsetParent;
		var offsetParentTagName = offsetParent != null ? offsetParent.tagName.toLowerCase() : "";

        if (__isIENew  && offsetParentTagName == 'td') {
		    res.y = element.scrollTop;
		}
		else {
		    res.y = element.offsetTop;
		}
    	
		var parentNode = element.parentNode;
		var borderWidth = null;

		while (offsetParent != null) {
			res.x += offsetParent.offsetLeft;
			res.y += offsetParent.offsetTop;
			
			var parentTagName = offsetParent.tagName.toLowerCase();	

			if ((__isIEOld && parentTagName != "table") || (__isFireFoxNew && parentTagName == "td")  || __isChrome) {		    
				borderWidth = __getBorderWidth(offsetParent);
				res.x += borderWidth.left;
				res.y += borderWidth.top;
			}
		    
			if (offsetParent != document.body && offsetParent != document.documentElement) {
				res.x -= offsetParent.scrollLeft;
				res.y -= offsetParent.scrollTop;
			}


			//next lines are necessary to fix the problem with offsetParent
   			if (!__isIE && !__isOperaOld || __isIENew) {
    			while (offsetParent != parentNode && parentNode !== null) {
					res.x -= parentNode.scrollLeft;
					res.y -= parentNode.scrollTop;
					if (__isFireFoxOld || __isWebKit) {
						borderWidth = __getBorderWidth(parentNode);
						res.x += borderWidth.left;
						res.y += borderWidth.top;
					}
    				parentNode = parentNode.parentNode;
    			}    
			}

   			parentNode = offsetParent.parentNode;
    		offsetParent = offsetParent.offsetParent;
		}
	}
    return res;
}

To use this function just pass your element in function's parameter and get the result object with left and top coordinates stored in x and y properties accordingly:

   
     var pos = getElementAbsolutePos(myElement);  
     window.alert("Element's left: " + pos.x " and top: " + pos.y);  
GetElementAbsolutePos function was tested on all most used browsers:
  • Internet Explorer 7.0 and higher
  • FireFox 2.x and FireFox 3.x.
  • Opera 9.x, 10.x
  • Chrome 5.0


Re: Absolute coordinates of DOM element within document

Thank you so much for posting this! It was a huge help!

Re: Absolute coordinates of DOM element within document

yes great function! have been looking for it in the net and found it here!! cheers man ;)

Re: Absolute coordinates of DOM element within document

thanks, love cut-and-paste helper functions

Re: Absolute coordinates of DOM element within document

It does not worked on FF3.
Best regards!

Re: Absolute coordinates of DOM element within document

Thanks! That is nice! This saves my time trying to figure out if there is a property for each element in an html document which holds it absolute position. You rock!

Re: Absolute coordinates of DOM element within document

Thanks a lot. It works like a charm. It saved a lot of time for me.

Grate code - spcially for firefox

Thanks for that code, it's really helpful

Re: Absolute coordinates of DOM element within document

Thanks for posting this  function. It is very helpful and saved lot of time.

Element position in Firefox

setting the position needs a unit in firefox .style.left = x + "px"

Element position in Firefox

I agree with you but there is no such assignment in our code ( .style.left = ... )

Re: Absolute coordinates of DOM element within document

The code says

borderWidth = kGetBorderWidth(parentNode);

Is that just a typo and it's supposed to be "__getBorderWidth(...)"?

Re: Absolute coordinates of DOM element within document

Hi,

You are totally right.
I have already fixed this typo in the code.

Re: Absolute coordinates of DOM element within document

Cool. Thanks.

Re: Absolute coordinates of DOM element within document

Works nice for FF and Chrome, but IE7 reports values: -1 for both x and y

Re: Absolute coordinates of DOM element within document

Could you send an example of such page to techsupport@korzh.com?

Re: Absolute coordinates of DOM element within document

The code has been updated. Chrome 5.x and Opera 10.x are supported now.

Re: Absolute coordinates of DOM element within document

just returns 0 in my ie8 and firefox 3.6

Re: Absolute coordinates of DOM element within document

I suppose you have passed an element's ID not the element itself - that is why it returns wrong result. I have added a little piece of code at the beginning of this function to make it work both for elements themselves and for their IDs as well. If it is not the case then please send me some testing page where the problem occurs.

Re: Absolute coordinates of DOM element within document

why do you say its not easy?

just copy paste from here ;)

Thanks

Works great - but gets IE8 slightly wrong

Sergiy,

many thanks indeed, this is by far the best I've found on the net. I'm using it to display custom tooltips (own div) fixed directly over select boxes - don't want to have tips follow the cursor for select boxes since the option list pops up and then things get confusing. I've tested with FF, Safari, Opera, Chrom, IE6, IE7 - all fine. But with IE8 the tip is consistently being positioned too high, obviously something in my container hierarchy is not being summed correctly. I would be most grateful if you could find a fix, the place where you can see it happening is here:

http://timreeves.de/proto/hauser2010/allgemein/buchen.php?r=r0NPK0806

The misplaced tips are displayed for some of the dropdowns in the search box (bottom left) and also on the page itself (field "Land"). The good news is, the tips of the dropdowns in the scrollable content area still get the right position (except IE8, of course) when the page is scrolled down a bit (the tips move up correspondingly, even in IE8).

Regards - and again many thanks - Tim

Works great - but gets IE8 slightly wrong

As I see it works wrong only for all combo-box elements on your page. I will try to fix this issue but not sure if it will be possible.

Re: Absolute coordinates of DOM element within document

Hallo Sergiy,
wow - what a quick answer - many many thanks.
I had obviously not explained enough: The JavaScript magic involved is all in a file "src/startup.js". The fixed position tips in the main flyout menu use a simpler (i.e. faster) function of my own - findMenuPos() - because there is no way for them to be scrolled down; most of the other tips are positioned at and to follow the cursor (see getmouseposition() from lixlpixel - works a dream). So in fact, your getElementAbsolutePos() is ONLY used for the combo-boxes - to get a tip fixed above the box which is still in the right place when the content is scrolled down.
Hope this makes it easier to find the problem - my gut feeling is that some relative-positioned div, or some borders, or some such, is just is not getting counted right.
Best regards, Tim

Re: Absolute coordinates of DOM element within document

It seems we have fixed this code. Check the latest version I've just published.

Re: Absolute coordinates of DOM element within document

Hi Sergiy,

many thanks for your work! While I see that a few lines of code have been changed, I'm sorry to say that my problem is not fixed, IE8 still has the same positioning error - about 15px too high. Your new version is incorporated into the website-prototype with the page:

http://timreeves.de/proto/hauser2010/allgemein/buchen.php?r=r0NPK0806

whereby the same typo as Phil mentioned above crept back in (I've corrected it in my JS file): borderWidth = kGetBorderWidth(parentNode);

Best regards, Tim

Re: Absolute coordinates of DOM element within document

It's a pity to hear. We've found a bug in our code and fixed it, so I supposed your problem should disappear as well. Unfortunately not. Could you create a small testing page where this problem occurs permanently, so we will be able to catch and fix the problem?

Re: Absolute coordinates of DOM element within document

Sergiy, thanks for sharing this work!

For those who are using the build in .NET webBrowser component in C# (Systems.Windows.Forms.WebBrowser) and would like to calculate an absolute position of an HTML element, then you may be able to use this method that I have translated from Sergiy's Javascript into C#

        private static Point GetHtmlElementAbsolutePosition(HtmlElement element)
        {
            //Source: http://blogs.korzh.com/progtips/2008/05/28/absolute-coordinates-of-dom-element-within-document.html
            Point pos = new Point();
            if (element != null)
            {
                pos.X = element.OffsetRectangle.Left;
                HtmlElement offsetParent = element.OffsetParent;
                string offsetParentTagname = offsetParent != null ? offsetParent.TagName.ToLower() : "";

                if (offsetParentTagname == "td")
                {
                    pos.Y = element.ScrollTop;
                }
                else
                {
                    pos.Y = element.OffsetRectangle.Top;
                }
                HtmlElement parentNode = element.Parent;

                while (offsetParent != null)
                {
                    pos.X += offsetParent.OffsetRectangle.Left;
                    pos.Y += offsetParent.OffsetRectangle.Top;

                    if (offsetParent != _winForm.MyBrowser.Document.Body && offsetParent.Parent != null)
                    {
                        pos.X -= offsetParent.ScrollLeft;
                        pos.Y -= offsetParent.ScrollTop;
                    }

                    while (offsetParent != parentNode && parentNode != null)
                    {
                        pos.X -= parentNode.ScrollLeft;
                        pos.Y -= parentNode.ScrollTop;
                        parentNode = parentNode.Parent;
                    }
                    parentNode = offsetParent.Parent;
                    offsetParent = offsetParent.OffsetParent;
                }
            }
            return pos;
        }
It is tested on a VS2010 installation with IE8

Re: Absolute coordinates of DOM element within document

Hi, first of all, great work!

I have the following problem: I have a centered div with multiple children divs and i am using the function to retrieve the inner divs position.

The function works great except for the non-visible (out of window) divs: while in the visible ones i get a 340px left , in the non visible i get 333px left. the top is ok. The divs are all aligned the same.

Do you have any idea whats wrong? I have the exact same problem using the jquery ui position function :\

Any help would be appreciated :)

Re: Absolute coordinates of DOM element within document

Unfortunately we can't help you with it. It's possibly by DOM's design.

Re: Additional

There is a problem when same HtmlElement is inside a iframe... You function returns position in this frame. I was add a little code to the end of your procedure before "return pos;" : HtmlElement frame = element.Document.Window.WindowFrameElement; if (frame != null) { Point fp = GetHtmlElementAbsolutePosition(frame); pos.X += fp.X; pos.Y += fp.Y; } So we have absolute position even if it is in alot of frames :)

Add a comment Send a TrackBack