65.9K
CodeProject 正在变化。 阅读更多。
Home

DOM 元素的绝对定位

starIconstarIconstarIconstarIconstarIcon

5.00/5 (19投票s)

2009年4月21日

CPOL

2分钟阅读

viewsIcon

130172

downloadIcon

718

一个 JavaScript 函数, 用于获取 DOM 元素在文档中的绝对坐标。

引言

有时(尤其是在 AJAX 项目中),需要获取 DOM 元素在当前文档中的“绝对”位置。例如,如果希望在另一个元素的位置(或带有一定偏移量)上显示某个隐藏的 DIV 对象,则需要这种“绝对”位置。我们在其中一个项目中使用了此函数,以在文本标签下方显示弹出菜单。

解决方案

可以使用诸如 style.leftstyle.topoffsetLeftoffsetTop 之类的属性来获取(或设置)元素在其父元素中的位置。因此,要获取元素在文档中的绝对位置,应该向上移动到元素的树中,并添加所有父元素的位置(除了最新的 document 元素)。

然而,这并不容易。仍然存在一些问题

  1. 首先,需要考虑父元素中可能的滚动,并相应地减少结果。
  2. 其次,不同浏览器(一如既往 :-( )的行为存在一些差异。对于 Internet Explorer,我们始终只需从元素 offsetParent 属性中存储的对象中减去滚动位置。但对于 FireFox,还需要考虑所有可通过 parentNode 属性访问的父元素。
  3. 最后,应该考虑某些父元素的边框宽度。不幸的是,这项任务并不像想象的那么容易,尤其是在 Internet Explorer 中。

所以,这是我们得到的函数

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;
}

要使用此函数,只需将您的元素作为函数的参数传递,并获取结果对象,其中 xy 属性分别存储左侧和顶部的坐标

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

getElementAbsolutePos 函数已在最常见的浏览器上进行了测试

  • Internet Explorer 6.0 及更高版本
  • Firefox 2.x 及更高版本
  • Opera 9.x 及更高版本
  • Google Chrome

历史

  • 2009 年 4 月 21 日:初始发布
  • 2009 年 4 月 28 日:文章已更新
  • 2011 年 4 月 20 日:文章已更新
© . All rights reserved.