Click here to Skip to main content
15,861,168 members
Articles / Web Development / XHTML

Absolute Position of a DOM Element

Rate me:
Please Sign up or sign in to vote.
5.00/5 (20 votes)
22 Apr 2011CPOL2 min read 122.5K   698   45   23
A JavaScript function to get the absolute coordinates of a DOM element within a document

Introduction

Sometimes (especially in AJAX projects), it is necessary to get the position of some DOM element in "absolute" coordinates within the current document. For example, such an "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 one of our projects to show a popup menu under a text label.

The Solution

Such properties as style.left, style.top, or offsetLeft, offsetTop can be used to get (or set) the position of an element within its parent. So, to get the element's absolute position within the document, we should move upwards on the element's tree and add the position of all the 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 the element's parents and decrease our result accordingly.
  2. Second, there are some distinctions in the behavior of different browsers (as usual :-( ). For Internet Explorer, we always just subtract the scrolling position of the object stored in the element's offsetParent property. But for FireFox, we also need to take into consideration all the parents accessible by the 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.

So, here is the function we get:

JavaScript
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(element) {
	var res = new Object();
	res.x = 0; res.y = 0;
	if (element !== null) { 
		if (element.getBoundingClientRect) {
			var viewportElement = document.documentElement;  
 	        var box = element.getBoundingClientRect();
		    var scrollLeft = viewportElement.scrollLeft;
 		    var scrollTop = viewportElement.scrollTop;

		    res.x = box.left + scrollLeft;
		    res.y = box.top + scrollTop;

		}
		else { //for old browsers
			res.x = element.offsetLeft;
			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 || __isChrome) && 
						parentTagName == "td")) {		    
					borderWidth = kGetBorderWidth
							(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 = 
						     kGetBorderWidth(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 the function's parameter and get the result object with the left and top coordinates stored in the x and y properties accordingly:

JavaScript
var pos = getElementAbsolutePos(myElement);
window.alert("Element's left: " + pos.x + " and top: " + pos.y);

The getElementAbsolutePos function was tested on the most common browsers:

  • Internet Explorer 6.0 and higher
  • Firefox 2.x and higher
  • Opera 9.x and higher
  • Google Chrome

History

  • 21st April, 2009: Initial post
  • 28th April, 2009: Article updated
  • 20th April, 2011: Article updated

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Founder Korzh.com
Ukraine Ukraine
Software developer and entrepreneur.

Main projects:
* EasyQuery - ad-hoc data filtering UI for .NET applications;
* Localizer - localization tool kit for Delphi projects;

Comments and Discussions

 
Praisenice article Pin
Member 1124779614-Jun-17 9:34
Member 1124779614-Jun-17 9:34 
SuggestionBugfix for Chrome Pin
enhzflep4-Oct-12 21:34
enhzflep4-Oct-12 21:34 
BugIn Chrome and Safari (webkit browsers), the code doesn't work if the page has been scrolled. Pin
Member 87787201-Apr-12 11:52
Member 87787201-Apr-12 11:52 
GeneralRe: In Chrome and Safari (webkit browsers), the code doesn't work if the page has been scrolled. Pin
Sergiy Korzh3-Apr-12 19:14
professionalSergiy Korzh3-Apr-12 19:14 
QuestionDoesn't seem to work with Chrome. Pin
Magnamus25-Oct-11 2:13
Magnamus25-Oct-11 2:13 
AnswerRe: Doesn't seem to work with Chrome. Pin
Sergiy Korzh25-Oct-11 21:55
professionalSergiy Korzh25-Oct-11 21:55 
GeneralRe: Doesn't seem to work with Chrome. Pin
Member 104416833-Dec-13 0:58
Member 104416833-Dec-13 0:58 
GeneralThanks !!!! Pin
Member 253297130-Jun-11 23:23
Member 253297130-Jun-11 23:23 
GeneralRealy usefull thanks! Pin
julio.soares10-May-11 21:36
julio.soares10-May-11 21:36 
GeneralVery cool. Pin
237412-May-11 12:36
237412-May-11 12:36 
GeneralgetBoundingClientRect Pin
Đỗ Hồng Ngọc24-Apr-11 23:31
professionalĐỗ Hồng Ngọc24-Apr-11 23:31 
GeneralRe: getBoundingClientRect Pin
Sergiy Korzh25-Apr-11 1:40
professionalSergiy Korzh25-Apr-11 1:40 
GeneralFunction not working in new browsers (IE 9, Firefox 4) Pin
vitor salgado11-Apr-11 12:20
vitor salgado11-Apr-11 12:20 
GeneralRe: Function not working in new browsers (IE 9, Firefox 4) Pin
Sergiy Korzh20-Apr-11 8:43
professionalSergiy Korzh20-Apr-11 8:43 
GeneralMy vote of 5 Pin
ashokmk29-Mar-11 7:09
ashokmk29-Mar-11 7:09 
GeneralThanks Pin
Sabarinathan A2-Mar-10 23:18
professionalSabarinathan A2-Mar-10 23:18 
GeneralBest JS article EVER Pin
Steve Westbrook29-Apr-09 4:32
Steve Westbrook29-Apr-09 4:32 
QuestionVisibility of DOM element? Pin
ChiPlastique28-Apr-09 10:32
ChiPlastique28-Apr-09 10:32 
AnswerRe: Visibility of DOM element? Pin
Sergiy Korzh29-Apr-09 10:15
professionalSergiy Korzh29-Apr-09 10:15 
QuestionNice, but discrepancies in material provided Pin
gladtobegrey27-Apr-09 21:45
gladtobegrey27-Apr-09 21:45 
AnswerRe: Nice, but discrepancies in material provided Pin
Sergiy Korzh28-Apr-09 20:19
professionalSergiy Korzh28-Apr-09 20:19 
GeneralNice Pin
vcshaman21-Apr-09 20:29
vcshaman21-Apr-09 20:29 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.