Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Getting position of mouse within a specified element

11 views
Skip to first unread message

Rostov

unread,
Apr 30, 2003, 3:56:07 PM4/30/03
to
Here's something I'm puzzling over: on a click, I want to capture the
position of the mouse. But not just the position of the mouse in the
window, nope (that would be too easy). I want the position of the
mouse within a specified HTML element -- that is, the mouse
coordinates relative to the top left corner of the specified element.

I know that if you absolutely position an element, you could do this
by subtracting the element's left/top style properties from the window
x/y. However, it's my understanding that if you want to leave the
element in normal flow, you can't count on those style properties
being set....

Any ideas?

DU

unread,
Apr 30, 2003, 7:15:42 PM4/30/03
to
Rostov wrote:
> Here's something I'm puzzling over: on a click, I want to capture the
> position of the mouse. But not just the position of the mouse in the
> window, nope (that would be too easy). I want the position of the
> mouse within a specified HTML element -- that is, the mouse
> coordinates relative to the top left corner of the specified element.
>

For MSIE 5+, event.offsetX and event.offsetY.
Opera 7's support for the offsetX and offsetY property is buggy: it's
wrong over text node and it's not relative to the client area (border
shouldn't be included).
http://www10.brinkster.com/doctorunclear/BrowserBugsSection/Opera7Bugs/Opera702Bugs.html

For NS 6.2+, there is no equivalent to offsetX and offsetY.

> I know that if you absolutely position an element, you could do this
> by subtracting the element's left/top style properties from the window
> x/y.

No. If the element is absolutely positioned or relatively positioned,
you can still access the offsetX and offsetY event property value in
MSIE 5+.
For Mozilla-based browsers, you would only need to use evt.layerX and
evt.layerY for absolutely or relatively positioned elements.

However, it's my understanding that if you want to leave the
> element in normal flow, you can't count on those style properties
> being set....
>
> Any ideas?

You can. Here's a way to get the equivalent of MSIE 5+ event.offsetX/Y
properties in Mozilla-based browsers:

var Element = evt.target ;
var CalculatedTotalOffsetLeft = CalculatedTotalOffsetTop = 0 ;
while (Element.offsetParent)
{
CalculatedTotalOffsetLeft += Element.offsetLeft ;
CalculatedTotalOffsetTop += Element.offsetTop ;
Element = Element.offsetParent ;
} ;
var OffsetXForMozillaBasedBrowsers = evt.pageX -
CalculatedTotalOffsetLeft ;
var OffsetYForMozillaBasedBrowsers = evt.pageY - CalculatedTotalOffsetTop ;

I suggest you go to the javascript section of my homepage and try the
different interactive demos on window and event, event.offsetX/Y,
evt.layerX/Y. You must use the proper browser, otherwise, you'll be
redirected to the proper page.

DU
---------------------------
Javascript and Browser bugs:
http://www10.brinkster.com/doctorunclear/

Richard Cornford

unread,
Apr 30, 2003, 9:30:14 PM4/30/03
to
Rostov wrote in message ...

>Here's something I'm puzzling over: on a click, I want to
>capture the position of the mouse. ...

>I want the position of the mouse within a specified HTML
>element -- that is, the mouse coordinates relative to
>the top left corner of the specified element.
<snip>

While I was knocking this page together DU posted some of the
alternatives. Browser computability is an issue that he raised and I
have found that the widest range of browsers can be addressed by
attempting to reed the mouse position relative to the whole HTML
document (not the visible area (possibly scrolled)) and the element
position also relative to the whole HTML document.

Obviously, if these two positions can be accurately acquired the
difference would be the offset into the element. Accuracy is an issue,
Netscape Opera and Mozilla seem to be accurate but my IE 4 is a couple
of pixels out. I have not tried IEs 5 or 6 as it is too late at night to
start switching on the computers on which they are installed.

The following HTML page has a function that attempts to return the
position of an HTML element. Its approach is virtually identical to the
one DU presented, except that it attempts to do a bit of fall-back for
Netscape 4.n (not that there are many elements on Netscape 4 that will
report their position (or support onmousedown events for that matter)).

The page also features a JavaScript (singleton) class that is a slightly
cut down version of an evolving class that I am designing to provide a
common interface for mouse position collection, especially with
mousemove events. It is probably a bit over the top for this application
(better suited to constant mouse position recording with onmousemove).

I have used DIV elements for convenience but you should try swapping the
elements to whatever type you are interested in. The co-ordinates are
reported in the window status line so you will need to be using a
browser that has an updateable status bar (and is set to allow
JavaScript to update the status), or modify the page to report the
co-ordinates in <input type="text"> elements.

<html>
<head>
<title></title>
<script type="text/javascript">

function totalElementXYOffset(obj){
var xOffset, yOffset, elParent;
if(obj){
if(typeof obj.offsetTop == 'number'){
yOffset = obj.offsetTop||0;
xOffset = obj.offsetLeft||0;
elParent = obj.offsetParent;
while(elParent){
yOffset += elParent.offsetTop||0;
xOffset += elParent.offsetLeft||0;
elParent = elParent.offsetParent;
}
}else if(typeof obj.top == 'number'){ //some Net 4 DIVs
yOffset = obj.top;
xOffset = obj.left;
}else if(typeof obj.y == 'number'){ //Net 4 IMG & A
yOffset = obj.y;
xOffset = obj.x;
}else{ //information is not available
return null;
}
return {x:xOffset,y:yOffset};
}
}

var MousePosition = function(){
var theOne = null;
var XY = {x:0,y:0};
var readScroll = {scrollTop:0,scrollLeft:0};
var lastTarget = null;
var eventInited = false;
var posReadX = 'pageX';
var posReadY = 'pageY';
function mm(e){
e = e||window.event;
XY.x = e[posReadX];
XY.y = e[posReadY];
lastTarget = e.target||e.srcElement;
};
function initEvent(ev){
ev = ev||window.event;
if(ev){
posReadX = ((typeof ev.pageX == 'number')?
'pageX':'clientX');
posReadY = ((typeof ev.pageY == 'number')?
'pageY':'clientY');
/* IE only correction for scroll position */
/*@cc_on @*/
/*@if (@_jscript_version >= 3)
if(posReadX == 'clientX'){
if((window.document.compatMode)&&
(window.document.compatMode == 'CSS1Compat')&&
(window.document.documentElement)){
readScroll = window.document.documentElement;
}else if(window.document.body){
readScroll = window.document.body;
}
}
@end @*/
/*
This block of code was from this class's original
application as a mouse-move following object and is
not needed for this application!

window.document.onmousemove = mm;
if(window.document.captureEvents){
window.document.captureEvents(Event.MOUSEMOVE);
};
*/
mm(ev);
eventInited = true;
}
};
return function(ev){ //constructor.
if(!eventInited)initEvent(ev);
if(theOne)return theOne;
this.getPageX = function(){
return readScroll.scrollLeft + XY.x;
};
this.getPageY = function(){
return readScroll.scrollTop + XY.y;
};
this.getPageXY = function(){
return {x:this.getPageX(),y:this.getPageY()};
};
this.getTarget = function(){
return lastTarget;
};
this.toString = function(){
return 'MousePosition [object]';
};
this.upDate = function(ev){
if(eventInited){
mm(ev);
}else{
initEvent(ev);
}
return this;
};
theOne = this;
};
}(); //simultaneously define and call (one-off)!

var posObject;
var clickCount = 0;
function reportPos(ev){
if(posObject || (posObject = new MousePosition())){
posObject.upDate(ev);
var tpos = totalElementXYOffset(posObject.getTarget());
if(tpos){
window.status = 'OffsetX = '+
(posObject.getPageX() - tpos.x)+
' OffsetY = '+
(posObject.getPageY() - tpos.y)+
' click = '+(++clickCount);
}else{
window.status = 'tpos is null! click = '+(++clickCount);
}
}
}

</script>
</head>
<body>
<br><br><br><br><br>
<div onmousedown="reportPos(event);"
style="float:right;width:100px;height:100px;background-color:#0000FF;">
</div>
<br><br><br><br><br><br><br><br><br><br>
<div onmousedown="reportPos(event);"
style="float:right;width:100px;height:100px;background-color:#0000FF;">
</div>
</body>
</html>

Richard.


DU

unread,
May 1, 2003, 8:14:55 AM5/1/03
to
Richard Cornford wrote:
> Rostov wrote in message ...
>
>>Here's something I'm puzzling over: on a click, I want to
>>capture the position of the mouse. ...
>>I want the position of the mouse within a specified HTML
>>element -- that is, the mouse coordinates relative to
>>the top left corner of the specified element.
>
> <snip>
>
> While I was knocking this page together DU posted some of the
> alternatives. Browser computability is an issue that he raised and I
> have found that the widest range of browsers can be addressed by
> attempting to reed the mouse position relative to the whole HTML
> document (not the visible area (possibly scrolled)) and the element
> position also relative to the whole HTML document.
>

It all depens on the referenced coordinate system you (as a web
designer) need to use. Mouse coordinates could be relative to
- the user's monitor screen,
- the browser window (not directly),
- the browser inner viewport area (aka client area, content area,
rendering area and viewing area),
- the canvas area (as defined by W3C),
- the html document,
- the body node,
- the offsetParent,
- the parent element, etc..
There is no absolute unique self-sufficient coordinate system which fits
better than another coordinate system: it all depens on the scripter's
needs. I can even demonstrate that MSIE 6 for Windows does not return
the same offsetLeft value (and other offset* values) when it is in
backward compatible rendering mode and when it is in standards compliant
rendering mode.

> Obviously, if these two positions can be accurately acquired the
> difference would be the offset into the element. Accuracy is an issue,
> Netscape Opera and Mozilla seem to be accurate

As worded, even that is debattable. 3 examples for you.
1- Opera 6 had a particular way of returning event.clientX/clientY and
event.pageX/pageY; in Opera 7, these event property values are reversed,
are entirely opposite from what they were in version 6. In Opera 7, they
fixed a design flaw which was in Opera 6.
2- You may want to check the code I posted on Feb. 18th 2002 in this
newsgroup (subject line was "offsetLeft bug in IE5.5") where I was able
to demonstrate that the 3 most recent browsers (Opera 6.03, Mozilla
0.9.8 and MSIE 6 for windows) at that time were returning 4 different
offsetLeft values for a specific page, for the same, exact page.
3- As mentioned before, event.offsetX/event.offsetY do not return the
same values for an element with a non-zero border in Opera 7 and in MSIE
6 for Windows.

but my IE 4 is a couple
> of pixels out. I have not tried IEs 5 or 6 as it is too late at night to
> start switching on the computers on which they are installed.
>
> The following HTML page has a function that attempts to return the
> position of an HTML element. Its approach is virtually identical to the
> one DU presented, except that it attempts to do a bit of fall-back for
> Netscape 4.n (not that there are many elements on Netscape 4 that will
> report their position (or support onmousedown events for that matter)).
>
> The page also features a JavaScript (singleton) class that is a slightly
> cut down version of an evolving class that I am designing to provide a
> common interface for mouse position collection, especially with
> mousemove events. It is probably a bit over the top for this application
> (better suited to constant mouse position recording with onmousemove).
>
> I have used DIV elements for convenience but you should try swapping the
> elements to whatever type you are interested in. The co-ordinates are
> reported in the window status line so you will need to be using a
> browser that has an updateable status bar (and is set to allow
> JavaScript to update the status), or modify the page to report the
> co-ordinates in <input type="text"> elements.
>
> <html>

No doctype decl. Therefore, there is no need to test for
document.compatMode in this document. Recent browsers and old browsers
will all be in backward compatible rendering mode here.


You've got a problem right here. Opera 7 will return the true value for
ev.pageX/ev.pageY but Opera 6 will return the incorrect values for pageX
and pageY. So, somehow you need to be able to accurately discriminate
between the 2.


> /* IE only correction for scroll position */
> /*@cc_on @*/
> /*@if (@_jscript_version >= 3)
> if(posReadX == 'clientX'){
> if((window.document.compatMode)&&
> (window.document.compatMode == 'CSS1Compat')&&
> (window.document.documentElement))


There is no need for this sub-element in your boolean condition. Per
definition, if a browser supports compatMode and is in CSS1 standards
compliant rendering mode (document.compatMode == "CSS1Compat"), then it
does support document.documentElement.

DU

Richard Cornford

unread,
May 1, 2003, 8:02:21 PM5/1/03
to
DU wrote in message ...
<snip>

>It all depens on the referenced coordinate system you (as a web
>designer) need to use. Mouse coordinates could be relative to
>- the user's monitor screen,

That one is best left well alone. You have to start caring about where
the window is on the screen and the dimensions of its chrome (I have not
seen any approach that will consistently do either).

>- the browser window (not directly),
>- the browser inner viewport area (aka client area, content area,
>rendering area and viewing area),
>- the canvas area (as defined by W3C),
>- the html document,
>- the body node,
>- the offsetParent,
>- the parent element, etc..
>There is no absolute unique self-sufficient coordinate system which
>fits better than another coordinate system: it all depens on the
>scripter's needs. I can even demonstrate that MSIE 6 for Windows
>does not return the same offsetLeft value (and other offset*
>values) when it is in backward compatible rendering mode and when
>it is in standards compliant rendering mode.

I cannot, and would not, deny that my approach is relying on assumptions
that are far from necessarily valid. ie. 1. That the sum of the
offsetTop/Left values when chained until the offsetParent is null (or
undefined) will produce a result relative to the pixel in the top left
corner of the HTML page display area on an un-scrolled window. 2. The
event.pageX/Y or (on IE) the event.clientX/Y plus the pertinent
scrollTop/Left, produce a value relative to that same pixel.

I have found that acting on those two assumptions has apparently given
the results in the same co-ordinate system on Netscape/Gecko browsers
and Opera (5+) but there was that 2px discrepancy on IE. Not that 2px
makes a lot of difference if you are just positioning a DIV but I have
just found myself needing the same offset values as the OP so I have
been looking into this on IE.

It appears that the chained offsetTop/Left values are the same as
Netscape/Opera (relative to the top left pixel on the HTML page) while
the mouse co-ordinates are actually relative to the top left pixel of
the inner bevelled edge of the border around the viewport. Hence the
discrepancy. I also discovered that on IE the clientTop/Left values of
which ever of document.body or document.documentElement are relevant are
also the width of the inner bevelled edge.

That means that the discrepancy can be corrected on IE by either adding
the clientTop/Left values to the offsetTop/Left total or subtracting
them from the event.clientY/X plus scrollTop/Left values. Correction the
mouse co-ordinates seems like the better option as that brings IE into
line with Netscape/Opera and applying those corrections to my code only
involves 3 changes:-

Providing extra properties on the dummy 'readScroll' object (so non IE
browsers do not trip up when trying to read the values).

>> var readScroll = {scrollTop:0,scrollLeft:0};

var readScroll = {scrollTop:0,scrollLeft:0,clientLeft:0,clientTop:0};

- and changing the functions that report the X and Y co-ordinates:-

>> this.getPageX = function(){
>> return readScroll.scrollLeft + XY.x;
>> };
>> this.getPageY = function(){
>> return readScroll.scrollTop + XY.y;
>> };

this.getPageX = function(){
return readScroll.scrollLeft - readScroll.clientLeft + XY.x;
};
this.getPageY = function(){
return readScroll.scrollTop - readScroll.clientTop + XY.y;
};

>> Obviously, if these two positions can be accurately acquired the
>> difference would be the offset into the element. Accuracy is an
>> issue, Netscape Opera and Mozilla seem to be accurate
>
>As worded, even that is debattable. 3 examples for you.
>1- Opera 6 had a particular way of returning event.clientX/clientY
>and event.pageX/pageY; in Opera 7, these event property values are
>reversed, are entirely opposite from what they were in version 6. In
>Opera 7, they fixed a design flaw which was in Opera 6.

That is actually a bit perplexing as to date I have consistently been
getting typeof event.pageX/Y == 'undefined' on Opera <= 6 (5.12, 6.04 &
6.05) in any and all of its spoofing modes, along with values for
event.clientX/Y that seem consistent with pageX/Y on Netscape/Mozilla.
(Maybe it is time to download Opera 6.1)

I have noticed that Opera 7.02 has changed its previously wrong
clientX/Y
values to something more consistent with IE, but I was hoping to avoid
that problem by preferring pageX/Y when available. (Maybe it is time to
download Opera 7.1, which would make 8 versions of Opera, at least they
do seem to be moving in the right direction. ;-)

>2- You may want to check the code I posted on Feb. 18th 2002 in this
>newsgroup (subject line was "offsetLeft bug in IE5.5") where I was
>able to demonstrate that the 3 most recent browsers (Opera 6.03,
>Mozilla 0.9.8 and MSIE 6 for windows) at that time were returning 4
>different offsetLeft values for a specific page, for the same, exact
>page.
>3- As mentioned before, event.offsetX/event.offsetY do not return the
>same values for an element with a non-zero border in Opera 7 and in
>MSIE 6 for Windows.

It is general inconsistencies in the values reported for event
properties that has been encouraging me to concentrate on the pageX/Y
(and its equivalents) as they seem to offer the most consistent results
with the simplest decision making process. Especially now I know that I
am going to have to correct event.clientY/X with clientTop/Left as well
as scrollTop/Left to get event.pageY/X equivalents on IE browsers.

<snip>


>> <html>
>
>No doctype decl. Therefore, there is no need to test for
>document.compatMode in this document. Recent browsers and old
>browsers will all be in backward compatible rendering mode here.

No need for the compatMode test in this context, but I would not like my
code to fall over if it was moved to a real HTML page. I always feel
that if I post a page with a doctype I should also validate against its
DTD but with a page with only 20 HTML tags I rarely want to go to that
effort.

<snip>


>> var posReadX = 'pageX';
>> var posReadY = 'pageY';
>> function mm(e){
>> e = e||window.event;
>> XY.x = e[posReadX];
>> XY.y = e[posReadY];
>> lastTarget = e.target||e.srcElement;
>> };
>> function initEvent(ev){
>> ev = ev||window.event;
>> if(ev){
>> posReadX = ((typeof ev.pageX == 'number')?
>> 'pageX':'clientX');
>> posReadY = ((typeof ev.pageY == 'number')?
>> 'pageY':'clientY');
>
>
>You've got a problem right here. Opera 7 will return the true value
>for ev.pageX/ev.pageY but Opera 6 will return the incorrect values
>for pageX and pageY. So, somehow you need to be able to accurately
>discriminate between the 2.

As I said, to date I have only ever got typeof ev.pageX == 'undefined'
out of Opera <= 6 so this test has been discriminating. Though Opera 6.1
might have thrown a spanner in the works, I will have to check.

>> /* IE only correction for scroll position */
>> /*@cc_on @*/
>> /*@if (@_jscript_version >= 3)
>> if(posReadX == 'clientX'){
>> if((window.document.compatMode)&&
>> (window.document.compatMode == 'CSS1Compat')&&
>> (window.document.documentElement))
>
>There is no need for this sub-element in your boolean condition.
>Per definition, if a browser supports compatMode and is in CSS1
>standards compliant rendering mode (document.compatMode ==
>"CSS1Compat"), then it does support document.documentElement.

<snip>

True, it is however a one time execution so apart from the extra bytes
of download the cost is minimal. Granted it only covers the massively
unlikely event of a browser implementing IE style conditional
compilation and only vaguely approaching the W3C spec. OK, it *is* over
cautious, I will drop it!

Richard.


0 new messages