function getViewOffset(node, singleFrame) {
function addOffset(node, coords, view) {
var p = node.offsetParent;
coords.x += node.offsetLeft - (p ? p.scrollLeft : 0);
coords.y += node.offsetTop - (p ? p.scrollTop : 0);
if (p) {
if (p.nodeType == 1) {
var parentStyle = view.getComputedStyle(p, '');
if (parentStyle.position != 'static') {
coords.x += parseInt(parentStyle.borderLeftWidth);
coords.y += parseInt(parentStyle.borderTopWidth);
if (p.localName == 'TABLE') {
coords.x += parseInt(parentStyle.paddingLeft);
coords.y += parseInt(parentStyle.paddingTop);
}
else if (p.localName == 'BODY') {
var style = view.getComputedStyle(node, '');
coords.x += parseInt(style.marginLeft);
coords.y += parseInt(style.marginTop);
}
}
else if (p.localName == 'BODY') {
coords.x += parseInt(parentStyle.borderLeftWidth);
coords.y += parseInt(parentStyle.borderTopWidth);
}
var parent = node.parentNode;
while (p != parent) {
coords.x -= parent.scrollLeft;
coords.y -= parent.scrollTop;
parent = parent.parentNode;
}
addOffset(p, coords, view);
}
}
else {
if (node.localName == 'BODY') {
var style = view.getComputedStyle(node, '');
coords.x += parseInt(style.borderLeftWidth);
coords.y += parseInt(style.borderTopWidth);
var htmlStyle = view.getComputedStyle(node.parentNode, '');
coords.x -= parseInt(htmlStyle.paddingLeft);
coords.y -= parseInt(htmlStyle.paddingTop);
}
if (node.scrollLeft)
coords.x += node.scrollLeft;
if (node.scrollTop)
coords.y += node.scrollTop;
var win = node.ownerDocument.defaultView;
if (win && (!singleFrame && win.frameElement))
addOffset(win.frameElement, coords, win);
}
}
var coords = { x: 0, y: 0 };
if (node)
addOffset(node, coords, node.ownerDocument.defaultView);
return coords;
} (The optional second argument turns off recursing in parent frames, when set, so you get document-relative coordinates .)As I recently had a use for half of it -- computing Y positions -- I hacked out separate smaller getYOffset and getXOffset versions, and when I had those, it occurred to me that these ought to be properties in the
Element DOM interface and implemented behind the curtains, so we could simply write img.documentX, img.documentY, et cetera, or img.pageX and img.pageY, if we wanted the coordinates of the image, counting from the outer(most) surrounding parent window. Hacking up a mini-library for that was a breeze from these primitives:function documentX() { return getXOffset(this, 1); }
function documentY() { return getYOffset(this, 1); }
function pageX() { return getXOffset(this); }
function pageY() { return getYOffset(this); }
Node.prototype.__defineGetter__('documentX', documentX);
Node.prototype.__defineGetter__('documentY', documentY);
Node.prototype.__defineGetter__('pageX', pageX);
Node.prototype.__defineGetter__('pageY', pageY); So now you could write code looking like this to inspect coordinates of things hovered by the mouse:Hovered element: <input type="text" id="node-coords" /> <=
<input type="text" id="mouse-coords" />
<script src="http://ecmanaut.googlecode.com/svn/trunk/lib/getXOffset.js"></script>
<script src="http://ecmanaut.googlecode.com/svn/trunk/lib/getYOffset.js"></script>
<script>
var mouse = document.getElementById('mouse-coords');
var output = document.getElementById('node-coords');
var hovered = document.body, saved = hovered.style.outline || '';
hovered.addEventListener('mousemove', hovering, false);
function hovering(e) {
mouse.value = 'mouse @ '+ e.pageX + ', '+ e.pageY;
var node = e.target;
if (node === hovered) return;
var what = node.tagName +' @ ';
var where = node.pageX +', '+ node.pageY
output.value = what + where;
hovered.style.outline = saved;
saved = (hovered = node).style.outline;
hovered.style.outline = '1px dashed lightBlue';
}
</script>
Hovered element: <= Until this kind of ease gets into DOM 3 or 4 (we can hope, at least), your code is better off using getViewOffset instead, though, when you wanted both properties anyway:
// ...
var coords = getViewOffset(node);
var where = coords.x + ', '+ coords.y;
// ...