Mission Impossible - mouse position (quotation)

quotation: http://evolt.org/article/Mission_Impossible_mouse_position/17/23335/index.html

At the moment I am writing an Introduction to Events. As part of my study I wanted to create a generic,

simple script that detects the mouse coordinates at the time of the event. Because such a script can work in Netscape, Explorer, Opera, Konqueror and iCab, it should work in all these browsers.

But I failed. In fact the situation is so bad that I have reluctantly come to the conclusion that a cross–browser script for finding the mouse coordinates during an event must necessarily use a browser detect, something I truly loathe.

The problem is, believe it or not, that Explorer correctly follows the standard — or rather that Opera, Konqueror and iCab have en masse decided not to follow the standard — or maybe that the standard is in fact not the standard.

Detecting the mouse coordinates became such a complex matter, and the story contains such fundamental lessons about standardization, that I wrote this article.

In search of a reference point

But let’s begin at the beginning. We are looking for mouse coordinates.

The main question is “mouse coordinates relative to what?” If a mouse coordinate property has a value of 300 this always means “300 pixels from my reference point”, but it doesn’t tell us what this reference point is.

So first we have to decide which reference point we need and then search the properties to match from the six available pairs (see also my compatibility table).

Please note the difference between document and window. The window is the “viewport” the user has available to view the document in. The document is frequently far longer than the window; in that case we scroll it to get another portion of the document into our viewport.

  ---------------------
| Start of document |
-------------------------
| | X                 | |
| |                   | |
|<--- Browser window -->|
| |                   | |
-------------------------
|                   |
|  End of document  |
---------------------

For example, the coordinates of the X relative to the window would be 10,10 while its coordinates relative to the document would be 10,100.

What we need are the mouse coordinates relative to the document because we often want to place some kind of DHTML layer on or just next to the mouse position. DHTML layers also calculate their position relative to document. If we can find the mouse coordinates relative to the document we can immediately paste them into the top and left properties of the DHTML layer we wish to move, avoiding all kinds of trouble.

So where can we find these mouse coordinates relative to the document?

DOM implementation’s client area

The W3C DOM Level 2 Events standard specifies two property pairs for mouse coordinates: screenX/Y, the mouse coordinates relative to the entire computer screen, and clientX/Y, which are defined as

“the horizontal/vertical coordinate at which the event occurred relative to the DOM implementation’s client area.”

Now what, one wonders, is the “DOM implementation’s client area”? Because both Netscape 6 and Explorer define clientX/Y as the mouse coordinates relative to the window, it is commonly translated as window.

This means that to obtain the mouse position relative to the document we have to add the values of some unstandardized, browser specific scrolling offset properties to the coordinates relative to the window clientX/Y give us. Thus there is no standards compatible way to find this information. So far so bad, though it can be worked around.

The minor browsers

But it gets worse. Opera, Konqueror and iCab define clientX/Y as the mouse coordinates relative to the document, not to the window. So three important minor browsers disagree with Netscape 6 and Explorer and even with W3C, though all three have a good name for standards compliance.

We don’t see such incompatibilities often because the minor browsers can’t afford them. Although Netscape/Explorer code branching has been common for many years, few web developers would bother to write special code branches for the minor browsers. If a certain method or property is supported by a minor browser it must be exactly equal to the Microsoft property of Explorer or the W3C property of Netscape 6. Incompatibility with both of the big ones means that neither code branch will work and that scripts will not function correctly. Thus incompatibility is uncommon, usually caused by a bug.

But now three of them disagree with both big ones in the same way. This is a rare sight, so rare in fact, that we should sit up and take notice. Might they have a point? It is clear that their implementation of clientX/Y would greatly benefit web developers, if it were truly cross–browser.

Browser incompatibility

But it isn’t cross–browser, and in fact our chance at a properly written cross–browser script is destroyed. As we’ve seen Netscape 6 and Explorer support clientX/Y according to the standard: the properties contain the mouse coordinates relative to the window.

Netscape fortunately stores the coordinates relative to the document in another (non–standard) property pair, so we can avoid using clientX/Y. Again we see that browser vendors are willing to help us find the mouse position relative to the document.

That leaves Explorer as the single browser that does not give us the information we need, as the single browser for which we must follow the W3C specification to the last dot and comma. Yet another rare sight.

The standards are not the standards?

Or do we all misunderstand the standard and should “DOM implementation’s client area” be translated as document? That would be a blessing to web developers — and restore the natural order of standards compliance in browser–land.

But if the standard means document it hides this fact very carefully. On the other hand, if it means window it is pretty vague, too. And we all know what happens when standards are vague. Browser vendors create their own standards. That’s what has happened here.

So let’s go down the sewer for some dirty fixes.

pageX, pageY

The old Netscape event model contains the pageX/Y property pair, which gives the mouse coordinates relative to the entire document. Since this is exactly the information we’re looking for, we try to apply these properties.

pageX/Y is supported by Netscape 4 and 6 and sometimes by iCab. So for the benefit of these browsers we start up a bit of object detection. Does the browser actually support these properties? If so, use them.

function doSomething(e)
{
var posx = 0;
var posy = 0;
if (!e) var e = window.event;
if (e.pageX || e.pageY)
{
posx = e.pageX;
posy = e.pageY;
}
...

Not only have we made our script Netscape 4 compatible, we have also neatly evaded the clientX/Y problem in Netscape 6.

clientX, clientY

If the browser doesn’t support pageX/Y we have to use clientX/Y. If your page does not need to scroll you can ignore all compatibility questions. After all when the page hasn’t scrolled the coordinates relative to the window are the same as those relative to the document.

You just do

	...
else if (e.clientX || e.clientY)
{
posx = e.clientX;
posy = e.clientY;
}
// posx and posy contain the mouse position relative to the document
// Do something with this information
}

and you’re ready.

Browser detect

However, if the page can scroll we have some very serious problems. There are two options.

  1. If clientX/Y contain the coordinates relative to the document, we read them out and are ready.
  2. If clientX/Y contain the coordinates relative to the window, we read them out, add the scrolling offset of the page and are ready.

But how do we know which of these two options we have to use? I studied the remaining four property pairs to see if they offer a way out, but they don’t. I looked at the problem from a theoretical point of view: is it possible to ascertain the meaning of clientX/Y by reading out some screen properties like scrolling offset and window height and performing complex calculations? No go.

Therefore I’m forced to use a browser detect. I loathe it, but there is no other way. So let’s hold our noses and do it.

var isOpera = (navigator.userAgent.indexOf('Opera') != -1);
var isIE = (!isOpera && navigator.userAgent.indexOf('MSIE') != -1)

Remember that Opera can try to disguise itself as Explorer, which would be fatal to this script. Therefore we have to check for Opera first. Konqueror and iCab can disguise themselves too, by the way, but when they do they completely switch identity and are undetectable. Too Bad, cannot be helped.

Now we do

	...
else if (e.clientX || e.clientY)
{
posx = e.clientX;
posy = e.clientY;
if (isIE)
{
posx += document.body.scrollLeft;
posy += document.body.scrollTop;
}
}
// posx and posy contain
// the mouse position relative to the document
// Do something with this information
}

and the script works, for the moment.

(Note that if you use a DOCTYPE in Explorer 6, you may have to search for scrollTop in document.documentElement instead of document.body. I’m not yet sure of the details of this problem and in any case they are beyond the scope of this article.)

Drawbacks of a browser detect

This script is an interesting example of the drawbacks of browser detection as a regular programming technique. At the moment our dirty fix works fine for undisguised Explorer, Opera, Konqueror and iCab browsers, sure. But what about the future?

If any of the four browsers changes its clientX/Y implementation the script doesn’t work correctly any more — and if Explorer switched it would be downright disastrous.

You could of course rewrite your browser detect, but do you remember every single page you used the script on? And how about earlier versions of the browser that has changed? You’d have to write some more code to correctly draw the line between standard–compatible and non–standard–compatible versions. And would these versions be the same on all operating systems?

For such reasons I detest the impossible situation W3C’s vagueness and the browser vendors’ well–meaning but untimely intervention have created. I want to keep my code clean, but thanks to the mishandling of clientX/Y I have no choice but to accept the ugly navigator.userAgent mess.

The clientX/Y confusion is a blot on the otherwise excellent DOM Level 2 Events spec. Let’s hope for a speedy solution.

Peter-Paul Koch is a freelance browser expert and JavaScript guru living in Amsterdam, the Netherlands. He has been an Internet professional only since 1998, so he's definitely second generation.

His personal site is www.quirksmode.org. It includes the W3C DOM Compatibility Tables, currently the best resource on the Internet for this subject. Because of this research, he has been asked to co-edited chapters 17 to 19 of Flanagan's "JavaScript, the Definitive Guide", O'Reilly, 4th edition.

He is an administrator of the WDF-DOM mailing list, that counts most international JavaScript gurus among its members.

He has written the "Keep it Simple" column on Digital Web Magazine, as well as articles on A List Apart, Apple Developer Connection, and O'Reilly's Web Dev Center, in addition to Evolt.

object based browser identification

Submitted by Michael on April 16, 2002 - 04:54.

That's a nice article Peter, thanks for the insights :)

I think you're making it look a little too hard though by sniffing the userAgent string, it's indeed well known that this gives unreliable information. To identify Opera you could look for te window.opera object (I'm assuming here that this was also implemented in the Mac version of Opera). To identify Internet Explorer you might look for some unique property of the window, maybe showModalDialog. Using a combination of objects would make this even better.

This doesn't solve the problem that occurs when browser vendors change their implementation of clientX/Y in the future, but then again, what does?

Regards, Michael

login or register to post comments

Re: object based browser identification

Submitted by ppk on April 16, 2002 - 09:51.

Sure, that's possible. Finding a correct object for IE on both Win and Mac would be more difficult (document.all is also supported by other browsers), but it can be done.

However, I deliberately opted for a pure browser detect to show just how bad this whole situation is. We don 't need to see if the browser supports a certain object, we actually need to look which browser it is and then write our own logic ("if it's this browser, do that...")

I react to a bad situation by deliberately using bad code. It only serves to underline the messiness of it all.

login or register to post comments

What about Konqueror?

Submitted by bertilow on April 16, 2002 - 13:36.

A great article about an important problem. My hat off (if I had one)!

Now, from a practical point of view detecting the object "window.opera" is probably the better solution to finding Opera, and using code appropriate to it, but what about Konqueror? What object could we detect to find if we're dealing with Konqueror?

And what about iCab?

If we find such objects to detect the odd browsers, the problem might be solved from a practical point of view. Surely, as Peter Paul said, it's not very likely that we're going to get more browsers that have the guts to defy Mozilla and Explorer on a point like this.

login or register to post comments

Right On!

Submitted by MikeFoster on April 16, 2002 - 22:43.

Excellent article - that's what we expect from Paul, of course. I came to the same conclusions, and others, while developing my own cross-browser event object. This page was for testing it, have a look if you like. If you want to look under the hood, have a look at the file cbe_event.js, it's part of my library (LGPL). I lost a lot of sleep over that thing ;-) I wish I could test with Konq - but I only have access to Win machines. I'm unfamiliar with iCab, I'll have to check it out - thanks for the link. BTW, what's the rule here for external links? Are they allowed? Open in new window?

login or register to post comments

Re: What about Konqueror?

Submitted by Zlatev on April 26, 2002 - 05:47.

Detection: navigator.userAgent.indexOf("Konqueror")!=-1 e.clientX and e.clientY will do the job

login or register to post comments

put at least a warning

Submitted by Martin Tsachev on April 26, 2002 - 12:11.

Mike why don't you put warning messages on your site it crashed my Opera 6 on Windows 2000.

login or register to post comments

Re: What about Konqueror

Submitted by bertilow on April 26, 2002 - 20:00.

Detection: navigator.userAgent.indexOf("Konqueror")!=-1 e.clientX and e.clientY will do the job

No it won't. Browser sniffing based on the User Agent string is very frail. The correct way is to look for certain objects. The question about Konqeror is what special objects we can detect that only Konqueror has - like "document.layers" is for NS4, "document.all" is for MSIE, "window.opera" is for Opera, etc.

login or register to post comments

Thanks

Submitted by MikeFoster on April 26, 2002 - 20:02.

Thanks for the responses.

Zlatev: thanks for the tips. Currently, on window load I set is.gecko to true if Konqueror is detected. This was done for experimental support of Konq - since I can't test with it. I assume Konq supports as much dom2 as gecko.

shaggy: sorry about that. I test that site with Opera 6 on Win98 and Win2K and I don't see any problems. I have many Opera visitors and noone has reported anything. I'd be glad to help with the problem you're having if you'll describe the problem in more detail.

login or register to post comments

Re: What about Konqueror

Submitted by Martin Tsachev on April 26, 2002 - 20:18.

bertilow: Don't forget that Opera creates document.all when you set it to spoof IE. So that one is spoiled too althought it is the better method.

login or register to post comments

Detecting Opera

Submitted by bertilow on April 27, 2002 - 04:38.

shaggy: Don't forget that Opera creates document.all when you set it to spoof IE. So that one is spoiled too althought it is the better method.

That's why we whould use "window.opera" to detect Opera, and other truely browser-unique objects for other browsers (document.all alone will not be enough) - when we absolutely must do such browser detection. (I'm assuming "window.opera" actually exists in Opera and nowhere else.)

Normally not even this kind of browser detection is a good idea, but in this particular case it might be necessary. Normally we whould detect the objects we plan to use, and then use them. But if the actual implementations of an object differ this critically, we might have to try to find out which browser we're dealing with. But I do hope there's some other way of solving this problem.

login or register to post comments

Re: Detecting Opera

Submitted by Martin Tsachev on April 28, 2002 - 13:16.

That's exactly what I wanted to say - when you want to use the clientX property assuming that it is implemented in the same way in all browsers that create it you should check that one. Otherwise you have no other choice but do browser detection and that's bad.

login or register to post comments

Detecting browsers

Submitted by ppk on April 29, 2002 - 04:19.

Although there are several ways to detect browsers (as this discussion shows), the central point remains that it is bad habit to use any sort of browser detect, whether through navigator.userAgent or through browser-specific objects. It doesn't matter, it's equally bad since you don't actually plan on using the objects.

It's a mess.

login or register to post comments

So do we need *browser* detection at all?!

Submitted by Zlatev on April 30, 2002 - 14:19.

Don't we need just object detection - e.g. do we have document.all, document.layers, document.getElementById... BUT... -snip- But if the actual implementations of an object differ this critically, we might have to try to find out which browser we're dealing with. -snip- So where is the problem? The browser may mask itself, pretending to be someone else. (If somebody knows 100% secure way to differ IE5.5 from IE6 - just please tell it to me. I do not say that I actually need it... But who knows whether the next version of some browser will or will not (exactly) follow the standards of W3C. So IMHO the problem is not the script, the problem is not the objects (properties) we have - the problem is just the browser implementation of the standards. Not having standard behavior (of browser) - normally (web-developer) will get a headache. We do need BEHAVIOR detection mechanism.

login or register to post comments

or is it really object detection

Submitted by Martin Tsachev on April 30, 2002 - 17:47.

The problem with document.all and document.layers is that they are (most of the times) used to detect IE vs NN 4 and although it seems like we check an object what we do is a browser detection.

Note: As I have mentioned earlier Opera also creates document.all but only when you choose Identify as Internet Explorer - I personally think that this fools more JavaScripts than tweaking the user agent string.

login or register to post comments

Re: What about Konqueror?

Submitted by Zlatev on May 1, 2002 - 07:47.

You can find the following article interesting:
Another browser joins the HM family of supported browsers: Konqueror.
http://www.webreference.com/dhtml/column65/index.html

login or register to post comments

How to detect all of them

Submitted by tarquinwj on May 23, 2002 - 04:25.

I agree totally with your views Peter-Paul (is that what I am meant to call you). I hate having to detect browsers and I always use object detection, but I also got caught by this annoying 'feature'. For the sake of all the users who need a complete working solution (as current browsers stand . . .), here it is. In this case, there is an easier way to detect all of the browsers that do not follow the standard.

Many people have already mentioned window.opera, which usefully will always be available, even when Opera is set up to spoof as IE5. On your own site, you mentioned window.debug, provided by iCab. There is also a navigator property that Konqueror provides, no matter what mode it is in (I think, I don't have it to test).

/* works with IE 4+, NS4, NS6 and other Gecko, OmniWeb, Opera (4) 5+,
    iCab, IceBrowser, Esape 4, HotJava 3, Konqueror and, as long as they
    follow the standards, loads more */
    function findCoords(e) {
    var posX = 0, posY = 0;
    if( !e ) { e = window.event; } if( !e ) { return [ 0, 0 ]; }
    if( typeof( e.pageX ) == 'number' ) {
    posX = e.pageX; posY = e.pageY;
    } else {
    if( typeof( e.clientX ) == 'number' ) {
    posX = e.clientX; posY = e.clientY;
    if( document.body && !( window.opera || window.debug || navigator.vendor == 'KDE'; ) ) {
    if( typeof( document.body.scrollTop ) == 'number' ) {
    posX += document.body.scrollLeft; posY += document.body.scrollTop;
    }
    }
    if( document.documentElement && !( window.opera || window.debug || navigator.vendor == 'KDE' ) ) {
    if( typeof( document.documentElement.scrollTop ) == 'number' ) {
    posX += document.documentElement.scrollLeft; posY += document.documentElement.scrollTop;
    }
    }
    }
    }
    return [ posX, posY ];
    }

login or register to post comments

Or then again, it doesn't work in Ice

Submitted by tarquinwj on June 13, 2002 - 01:44.

That last version doesn't work in Ice because it provides both document.body.scrollTop & document.documentElement.scrollTop. This works:

/* works with IE 4+, NS4, NS6 and other Gecko, OmniWeb, Opera (4) 5+,
    iCab, IceBrowser, Esape 4, HotJava 3, Konqueror and, as long as they
    follow the standards, loads more */
    function findCoords(e) {
    var posX = 0, posY = 0;
    if( !e ) { e = window.event; } if( !e ) { return [ 0, 0 ]; }
    if( typeof( e.pageX ) == 'number' ) {
    posX = e.pageX; posY = e.pageY;
    } else {
    if( typeof( e.clientX ) == 'number' ) {
    posX = e.clientX; posY = e.clientY;
    if( document.body && ( document.body.scrollTop || document.body.scrollLeft ) && !( window.opera || window.debug || navigator.vendor == 'KDE'; ) ) {
    posX += document.body.scrollLeft; posY += document.body.scrollTop;
    }  else {
    if( document.documentElement && ( document.documentElement.scrollTop || document.documentElement.scrollLeft ) && !( window.opera || window.debug || navigator.vendor == 'KDE' ) ) {
    posX += document.documentElement.scrollLeft; posY += document.documentElement.scrollTop;
    }
    }
    }
    }
    return [ posX, posY ];
    }

login or register to post comments

Here's what I'm using (3 lines).

Submitted by redeye on August 14, 2002 - 15:09.

I think the following should work except when Konq masquerades as IE. Corrections?
var posx = (document.all && !window.opera) ? event.x + document.body.scrollLeft : e.clientX;
    var posy = (document.all && !window.opera) ? event.y + document.body.scrollTop : e.clientY;
    if (ns) {posx += document.body.scrollLeft; posy += document.body.scrollTop;}
    

login or register to post comments

You have missed the point.

Submitted by tarquinwj on September 17, 2002 - 01:34.

Yours may be only three lines, but IT DOESN'T WORK IN ALL 4th GEN BROWSERS. That was one of the main points PPK was trying to get across. I could write lots of things in only three lines, but that doesn't make me a good programmer because I am still leaving out several of the browsers. Also, you have not just written three lines. You have missed out:
function findCoords(e) {
   if( !event ) { event = window.event; }
and the lines to return the coordinates once you have worked out what they are.

Also, yours WILL NOT WORK in IE6 when it is in 'standards compliant mode'.

Let me explain this to you a bit better: there are not just 4 browsers in this world. The following browsers can all find the mouse position, maybe even more: Internet Explorer 4,5,6, Netscape 4, Opera, Gecko engine (Mozilla, Netscape 6+ etc.), Konqueror, OmniWeb, Escape 4, ICEbrowser, WebTV, iCab.

As this script CAN work in all those, it should be made to work in all of those. If not, you are just being arrogant and ignoring people for no good reason, just because you are too lazy to take them into account.

Besides which, you have already made a fatal mistake:
(document.all && !window.opera) ? event.x
Stop testing for something then assuming something else. It is bad programming. You will cause errors in browsers that you do not know about.
if(typeof(event.x)!='undefined') ? event.x

Yours will also cause errors if the browser does not pass any event object, or does not pass the coordinates as properties of the event object.

I'm sorry to come down so hard on you (and for the shouting ...) but I do feel that even after reading the article, you have missed most of the points that PPK was trying to make.

As a separate point, I made a mistake; ICEbrowser also made up window.debug and will test positive for iCab using what I showed above. To sest for iCab, you have to use
( window.ScriptEngine && ScriptEngine().indexOf( 'InScript' ) + 1 )

Here you go, I have even simplified it to make it as small as possible (and in theory work in Opera 4). Mine will also have no problems with browsers masquing their identity.

function findCoords(e) {
    if( !e ) { e = window.event; } if( !e || ( typeof( e.pageX ) != 'number' && typeof( e.clientX ) != 'number' ) ) { return [ 0, 0 ]; }
    if( typeof( e.pageX ) == 'number' ) { var posX = e.pageX; var posY = e.pageY; } else {
    var posX = e.clientX; var posY = e.clientY;
    if( !( ( window.navigator.userAgent.indexOf( 'Opera' ) + 1 ) || ( window.ScriptEngine && ScriptEngine().indexOf( 'InScript' ) + 1 ) || window.navigator.vendor == 'KDE' ) ) {
    if( document.documentElement && ( document.documentElement.scrollTop || document.documentElement.scrollLeft ) ) {
    posX += document.documentElement.scrollLeft; posY += document.documentElement.scrollTop;
    } else if( document.body && ( document.body.scrollTop || document.body.scrollLeft ) ) {
    posX += document.body.scrollLeft; posY += document.body.scrollTop;
    }
    }
    return [ posX, posY ];
    }
    }

login or register to post comments

W3Cs intentions

Submitted by jhuizer on January 9, 2003 - 11:44.

As you tell us in the article, the W3C DOM Level 2 Events standard specifies two property pairs for mouse coordinates: screenX/Y, the mouse coordinates relative to the entire computer screen, and clientX/Y, which are defined as ?the horizontal/vertical coordinate at which the event occurred relative to the DOM implementation?s client area.? I think it's quite obvious the clienX/Y must be relative to the document, not to the window - as the screenX/Y objects are allready relative to the screen - why else should clientX/Y exist ?? Ofcourse I agree that sentence itself is kind of worthless;

login or register to post comments

Re: W3Cs intentions

Submitted by ppk on January 9, 2003 - 15:00.

I think it's quite obvious the clienX/Y must be relative to the document, not to the window - as the screenX/Y objects are allready relative to the screen.

But the screen is not the same as the window. A window may take up the entire screen, or just a part of it. So it is still unclear what the 'DOM implementation's client area' means

login or register to post comments

Changed browser compatibility

Submitted by ppk on April 6, 2003 - 03:06.

The article above is now slightly outdated. My new research shows that Opera 7 now also supports pageX/Y and that iCab's support has stabilized. Therefore the browser detect is no longer necessary.

So first try pageX/Y, and if the browser doesn't support it (is IE), use clientX/Y and add the current scrolling offset of the page:

function doSomething(e)
    {
    var posx = 0;
    var posy = 0;
    if (!e) var e = window.event;
    if (e.pageX || e.pageY)
    {
    posx = e.pageX;
    posy = e.pageY;
    }
    else if (e.clientX || e.clientY)
    {
    posx = e.clientX + document.body.scrollLeft;
    posy = e.clientY + document.body.scrollTop;
    }
    // posx and posy contain
    // the mouse position relative to the document
    // Do something with this information
    }
    

login or register to post comments

Thanks a lot Peter!

Submitted by Zlatev on April 6, 2003 - 22:44.

Yet again thanks for the new information you have provided!

login or register to post comments

finding position 99% fat free :)

Submitted by rhundt on May 1, 2003 - 14:35.

Trying to find the x and y coordinates of an element on which an event was fired that works accross all browsers... One of the issues with traversing the DOM heirarchy via `obj.offsetParent' method is that some browsers - KHTML (Konqueror) for one - choose to ignore any borders applied to elements via CSS when calculating the offset. If you've got a 1 or 2 pixel border around one or two elements, then it's negligable, but I use CSS borders extensively. Another miscalculation of the offset relates to margin widths set on the BODY element.

Instead of traversing the DOM heirarchy to find the offsets of each element relative to it's parent, to simply find two event coordinates for each of X and Y respectively... the coordinates relative to the top left of the browser window (0:0) and the coordinates relative to the top left of the element on which the event was fired, and then subtract the two. That's the idea.

This has the advantage of negating the effect of margin widths and borders, because no matter where the browser thinks it's top-left corner is (i.e. inclusive of the margin width or not), it will be internally consistent.

The only caveat (one that's easily remedied) is that the all the elements in the heirarchy MUST be either relatively, or absolutely positioned. Now, I think this is a point that most people miss - the documentation - with particular reference to O'REILLY's good book on the subject - isn't terribly explicit on this, with respect to PageX and ClientX - depending on the browser - they do the same thing, that is: return the location relative to the top-left of the page.

So: HTMLElement.style.position = 'relative'

OR if you're using DIV only layout (which I believe you should), you can create a CSS class for DIV elements: div { position : relative; }

and that will apply itself to all your DIV tags.

Finally the code: Tested on Mozilla, IE5 and Konqueror

//============================================================
    // functions to find the X and Y coords of an element on which
    // an event occurred - the event object is passed
    //============================================================
    function findPosX(e)
    {
    var x = (e.offsetX) ? e.offsetX : e.layerX;
    var X = (e.pageX)   ? e.pageX   : e.clientX;
    var pos = X - x;
    return pos;
    }
    function findPosY(e)
    {
    var y = (e.offsetY) ? e.offsetY : e.layerY;
    var Y = (e.pageY)   ? e.pageY   : e.clientY;
    var pos = Y - y;
    return pos;
    }
    

my tuppence worth NOTE: the event object is passed into the handler function by default with Mozilla clones, but with IE, you need to do it manually: e = window.event, possibly needing the arguments array for cross browser magic

login or register to post comments

Re: finding position 99% fat free :)

Submitted by ppk on May 1, 2003 - 23:03.

Instead of traversing the DOM heirarchy to find the offsets of each element relative to it's parent, to simply find two event coordinates for each of X and Y respectively... the coordinates relative to the top left of the browser window (0:0) and the coordinates relative to the top left of the element on which the event was fired, and then subtract the two. That's the idea.

Nice idea, but I don't think this'll work with the code you show. Especially this rule:

 var X = (e.pageX)   ? e.pageX   : e.clientX;
    

won't work in all circumstances. As you can read in the main article, pageX and clientX are not equivalent, and you pretend they're just different names for the same property.

Have you tried this script in IE Windows on a page that's slightly scrolled down? I don't think it'll work in that case.

The only caveat (one that's easily remedied) is that the all the elements in the heirarchy MUST be either relatively, or absolutely positioned. Now, I think this is a point that most people miss - the documentation - with particular reference to O'REILLY's good book on the subject - isn't terribly explicit on this, with respect to PageX and ClientX - depending on the browser - they do the same thing, that is: return the location relative to the top-left of the page.

Sorry, but that's simply not true. clientX returns the coordinates relative to the browser window, whether the target has position: static, relative or absolute. I just retested it to be absolutely sure. Try the example at my test page. You'll see that clientY always gives the coordinates relative to the browser window. Scroll the page a bit down and retest for the full effect.

 

login or register to post comments

corrected

Submitted by rhundt on May 2, 2003 - 07:45.

PPK, you're right (of course) my mistake, I'll check my facts next time before I `cat /dev/random' to std out :)

Indeed, it is the offsetX and offsetY properties (and x, y) for IE that need to be `dynamically positioned' - not the clientX and clientY. Here is my test. I'm using the script I posted earlier to return the position of the top left corder of the button to position the drop down menu and then offsetting it with the height of the button. Note that you can drag the box containing the menu buttons, and their coordinates still get reported correctly - even if you drag it off the bottom of the page to force scrolling.

I DID find for that to work, that the containing elements needed to be dynamically positioned using CSS though.

My apologies for the misinformation, and thanks for pointing that out.

login or register to post comments

god thanks :)

Submitted by Fuduff on June 26, 2003 - 06:05.

I had several problems with this messy stuff.. but i see for you it was no impossible mission... i have a site where you have to scroll, and i put div as a description box near the cursor...
you can think of what happened when you scrolled down the page and tried to position the div... it had not worked until i saw this article... thx very much... if you want to test it, click here and klick on "maps".
--can't give direct link because i am moving the site soon--
on mouseover over the title of a map, you see the description.
before it was a mess, like i said...
thx very much :)

login or register to post comments

god thanks :)

Submitted by Fuduff on June 26, 2003 - 06:24.

I had several problems with this messy stuff.. but i see for you it was no impossible mission... i have a site where you have to scroll, and i put div as a description box near the cursor...
you can think of what happened when you scrolled down the page and tried to position the div... it had not worked until i saw this article... thx very much... if you want to test it, click here and klick on "maps".
--can't give direct link because i am moving the site soon--
on mouseover over the title of a map, you see the description.
before it was a mess, like i said...
thx very much :)

login or register to post comments

Don't worry about Konqueror..

Submitted by AkaXakA on July 16, 2003 - 13:10.

Konqueror, a KDE based browser runs only on Linux. However the core of Konq is KHTML and it's also used by safari !
There are a number of reasons why that is good news:

1) speeded up development; Apple is hiring a number of people (like david hyatt). All improvements to the core are flowing back to Konq. Improvements on Konq will most likely also find their way into Safari.

2) A basic rule of thumb will be: anything Safari can do Konq can do. This helps testing.


Thing is, the KHTML core will probably be so standards compliant, that you won't need to detect it. And anyway, because of the rapid development, it's probably very, very bad!

Remember what happend when we got "netscape" versions of webpages, designed for NS4, that flunked the superior NS6+7 ?
We don't want to go there again...

login or register to post comments

Problem concerning 0/0 mouseposition

Submitted by renkert.net on October 20, 2003 - 01:18.

Hello folks,

I found this article linked on Peters site on my search for some solutions for my problem and appreciate your efforts to solve this mouseposition problem. I am still working on a Layout- and Gui API for my CMS, and went into following trouble:
Based on Peters solution for getting the right X and Y mouseposition I tested this script in several browsers. Because of handling reasons, I made a background grid used on the side to have some " fix points" for testing purposes.
Whenever I touch a crossline with the mouse in Netscape and Opera, I get a result like 10,10 or 10,20 or 30,150 and so on. But if I try the same with IE, I always get 12,12 or 12,22 or 32,152 and so on, so I always have 2 Pixel more.
Question 1:
What is the 0,0 position ? Is this the first Pixel available on the upper left corner on my screen, or is this first Pixel the 1,1 position ?
Question 2:
Has anybody else made the same experience with this "problem" ?
Question 3:
Maybe I misunderstand something, but are there any layout settings in WIN or IE (maybe toolbar shadow a.s.o) which influence the 0,0 Position ?

You can test the script here: Test Mousetrack
In this example my 0,0 position in IE always touches the toolbar...the background grid was made based on my theory, that the first pixel available on the upper left corner has position 0,0 ! Maybe somebody has some ideas to solve this problem....

login or register to post comments

Another thx

Submitted by emorgan on October 26, 2005 - 09:45.

Just to say I too had a lot of problems with this, problem solved by reading a well written/reasoned article/discussion. thx

login or register to post comments

W3C is not ambiguous

Submitted by shelby on January 11, 2006 - 01:39.

You've got the problem turned on it's head. Let me re-orient the perspective.

"...relative to the DOM implementation's client area" is quite clear. DOM = Document Object Model. The Document Object Model's client area is not the window model's client area.

The fact that Netscape 6 emulates Internet Explorer's incorrect implementation of Event.clientX/clientY, could be a reflection of striving for script compatibility with the dominant browser.

And note that Internet Explorer does not even implement DOM Layer 2 Event, thus IE's event.clientX/Y is a different animal (interface) than Event.clientX/Y. This is detectable if the input Event to listener is null.

Thus the correct code without a browser detect is:

function listener( e )
    {
    var docX, docY;
    if( e )
    {
    if( typeof( e.pageX ) == 'number' )
    {
    docX = e.pageX;
    docY = e.pageY;
    }
    else
    {
    docX = e.clientX;
    docY = e.clientY;
    }
    }
    else
    {
    e = window.event;
    docX = e.clientX;
    docY = e.clientY;
    if( document.documentElement
    && ( document.documentElement.scrollTop
    || document.documentElement.scrollLeft ) )
    {
    docX += document.documentElement.scrollLeft;
    docY += document.documentElement.scrollTop;
    }
    else if( document.body
    && ( document.body.scrollTop
    || document.body.scrollLeft ) )
    {
    docX += document.body.scrollLeft;
    docY += document.body.scrollTop;
    }
    }
    }
    

Also, often what one wants is the position relative to the target of the event (Event.target for W3C browsers and event.srcElement for IE), then use Event.layerX/Y for Mozilla and event.OffsetX/Y for IE. Have not tested this. Do other W3C browsers support the Mozilla extentions Event.layerX/Y?

login or register to post comments

Re: Problem concerning 0/0 mouseposition

Submitted by housty on January 20, 2007 - 01:02.

I figured this out in IE.. if you read document.body.style.borderWidth after document.onload you will see that IE has it set to "medium" simply change this to "none" and you have the same x:y pos as mozilla

login or register to post comments

Having a simpler problem

Submitted by joenemeth on May 28, 2007 - 19:10.

I'm having a much simpler (but related) problem, and this is the closest I've seen in my web searches to even talking about it. The silence tells me there is probably a simple solution, but I can't find it.

I want to place a popup window using JavaScript in a position relative to the client area of the initiating browser, in a browser-independent way. Let's say (0,0) for convenience - top left corner of the browser window client area.

Let's just stick to IE and FireFox for the moment.

First, I've discovered that if I do window.open(..."screenX=0,screenY=0"...) in FireFox, or window.open(..."screenLeft=0,screenTop=0"...) in IE, they seem to place the windows in the same place, the upper-left corner of the screen - the coordinates in both cases are absolute screen coordinates. Since the pop-up is an independent window that can be dragged anywhere, this makes sense. So presumably, all I need to do is compute the screen location of the client area for each browser, and I'll be in fine shape.

In IE, it's very easy to get the client area coordinates: it's simply (window.screenLeft, window.screenTop). Unfortunately, there doesn't appear to be any way in IE to get the coordinates of the entire browser window, but I'm not looking for that right now.

However, in DOM, there conversely does not appear to be any way to obtain the screen coordinates of the top-left corner of the browser client area, which depends on what toolbars, etc., the user has activated. The DOM (window.screenX, window.screenY) appears to be the top-left corner of the entire window (which I cannot get in IE). I've gone looking through the DOM hierarchy for something that corresponds to the location of this upper left client area corner, and can't find it.

Interestingly enough, DOM does supply both (innerWidth, innerHeight) and (outerWidth, outerHeight) for the window object, which describes the size of the client area (in two different ways!). But, alas, no (innerX, innerY) or (outerX, outerY) or (clientX, clientY) for the window object.

What have I missed?

login or register to post comments

Found one solution

Submitted by joenemeth on May 28, 2007 - 21:04.

I found a workaround: ugly, but it works in my case.

I noticed that the event object in DOM happens to supply both the screen and client coordinates of the event - the only object where I found both of these coordinates provided. As a result, I can simply compute:

(e.screenX-e.clientX, e.screenY-e.clientY)

as the screen coordinates of the client window itself. Fortunately, I'm doing this in response to a mouse-click, so I actually have an event with valid coordinates to process....

login or register to post comments

One solution for all

Submitted by paulwratt on September 2, 2007 - 06:09.

.. have peeps here forgotten the bottom line of web dev as far as browsers go, "one at a time".

get it to work in one browser, then another, then another.. and so on.. (including versions)

THEN if you want "cross-browser" compatibility, code generically to catch the ones that you have not tested yet, put it together...

As for this issue, I suspect it is not much of a problem anymore, unless you are like me and like to support "old technology", in which case "it can be done" (see preceding paragraph)...

IE6 ISSUE

I have an issue with IE6 not firing an event on a mouse over in a scrolled IFRAME, until AFTER returning from that event, even tho the event object is available, with correct clientX/clientY, its still fails first time, as somehow grabbing the event object causes the mouse pointer to change (which is what I MUST test, its a help script)

if I REM my mouse out event it works. Some how a mouse out is sent directly after sending a mouse over, and only when scrolled, only on non button INPUT's and ONLY in IE.

The only thing I can think of is that it has something to do with windows modifying the default INPUT/SELECT boxes borders, which triggers an "upset" event. Like I said, there is an event object, it has the correct srcElement, and the correct clientX/clientY pair, but it will not render any output if a onMouseOut event "visible"/"inline" process is also enabled on the INPUT element AND the window is scrolled.

RECAP: Event object "fails" in onMouseOver the first time, changes the point "blinkingly", then sends a onMouseOut event, when inside a scrolled IFRAME window.

NOTE: I CAN scroll the window a fraction, ONLY when the CELL above it is no longer visible does it fail..

Paul

PS. I am lucky enough to have the poxy static onMouseOut when top.location.href!=location.href (for IE ONLY)

PPS. no other browsers have a problem with this..

login or register to post comments

posted on 2007-12-04 16:39  jerry data  阅读(547)  评论(0编辑  收藏  举报