When the mouse is moved over an element, I want to get the mouse coordinates of the cursor relative to the top-left of the element\'s content area (this is the area excludin
Here's a live example that uses an element_position()
function that is aware of padding and borders. I've added some extra padding and margins to your original example.
http://jsfiddle.net/Skz8g/4/
To use it, move the cursor over the brown area. The resulting white area is the actual canvas content. The brown is padding, the red is a border, and so on. In both this example and the one later on, the canvas x
and canvas y
readouts indicate the cursor position relative to canvas content.
Here's the code for element_position()
:
function getNumericStyleProperty(style, prop){
return parseInt(style.getPropertyValue(prop),10) ;
}
function element_position(e) {
var x = 0, y = 0;
var inner = true ;
do {
x += e.offsetLeft;
y += e.offsetTop;
var style = getComputedStyle(e,null) ;
var borderTop = getNumericStyleProperty(style,"border-top-width") ;
var borderLeft = getNumericStyleProperty(style,"border-left-width") ;
y += borderTop ;
x += borderLeft ;
if (inner){
var paddingTop = getNumericStyleProperty(style,"padding-top") ;
var paddingLeft = getNumericStyleProperty(style,"padding-left") ;
y += paddingTop ;
x += paddingLeft ;
}
inner = false ;
} while (e = e.offsetParent);
return { x: x, y: y };
}
The code should work properly in IE9, FF and Chrome, although I notice it is not quite right in Opera.
My original inclination was to use something like the e.offsetX/Y
properties because they were closer to what you want, and do not involve looping over nested elements. However, their behaviour varies wildly across browsers, so a bit of cross-browser finagling is necessary. The live example is here:
http://jsfiddle.net/xUZAa/6/
It should work across all modern browsers - Opera, FF, Chrome, IE9. I personally prefer it, but thought that although your original question was just about "getting mouse position relative to content area of an element", you were really asking about how to make the element_position()
function work correctly.
using jQuery:
function posRelativeToElement(elem, ev){
var $elem = $(elem),
ePos = $elem.offset(),
mousePos = {x: ev.pageX, y: ev.pageY};
mousePos.x -= ePos.left + parseInt($elem.css('paddingLeft')) + parseInt($elem.css('borderLeftWidth'));
mousePos.y -= ePos.top + parseInt($elem.css('paddingTop')) + parseInt($elem.css('borderTopWidth'));
return mousePos;
};
live example: http://jsfiddle.net/vGKM3/
The root of this is simple: compute the element's position relative to the document. I then drop the top & left padding & border (margin is included with basic positioning calculations). The internal jQuery code for doing this is based on getComputedStyle
and element.currentStyle
. Unfortunately I don't think there is another way...
The core of jQuery's .offset()
function, which gets an element's position relative to document:
if ( "getBoundingClientRect" in document.documentElement ) {
...
try {
box = elem.getBoundingClientRect();
} catch(e) {}
var body = doc.body,
win = getWindow(doc),
clientTop = docElem.clientTop || body.clientTop || 0,
clientLeft = docElem.clientLeft || body.clientLeft || 0,
scrollTop = (win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop ),
scrollLeft = (win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft),
top = box.top + scrollTop - clientTop,
left = box.left + scrollLeft - clientLeft;
return { top: top, left: left };
}else{
// calculate recursively based on .parentNode and computed styles
}
Theoretically, another way to do this would be, using the above positioning code:
position: relative
(or absolute) setposition: absolute; top:0px; left:0px;
In your element_position(e)
function, iterate through the hierarchy using parentNode
, get the padding, offsets and border using getComputedStyle(e, null).getPropertyValue(each_css)
, and sum them to the value of your x
and y
values before return.
There's a post proposing a cross-browser reading of styles here:
http://bytes.com/topic/javascript/answers/796275-get-div-padding
I am not sure if this is the best way, or most resource efficient...
But I would suggest getting X/Y for the canvas tag, width of the border, and padding and using them all together as the offset.
Edit:
Use offsetLeft and offsetTop
Reference: How to Use the Canvas and Draw Elements in HTML5
var x;
var y;
if (e.pageX || e.pageY) {
x = e.pageX;
y = e.pageY;
}
else {
x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
}
x -= gCanvasElement.offsetLeft;
y -= gCanvasElement.offsetTop;