Main Page Content
Mission Impossible Mouse Position
At the moment I am writing an
title="To my JavaScript Section" target="_blank">Introduction to Events.As part of my study I wanted to create a generic, simple script that detects the mouse coordinates atthe time of the event. Because such a script can work in title="Download Netscape 6" target="ppk">Netscape, title="Download Explorer 6" target="ppk">Explorer, title="Event handling improved dramatically between 5 and 6" target="ppk">Opera, title="An excellent, very standard conforming Linux browser" target="ppk">Konqueror and title="A good standard conforming Mac browser" target="ppk">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 eventmust necessarily use a browser detect, something I truly loathe.The problem is, believe it or not, that Explorer
correctly follows the standard — or rather thatOpera, Konqueror and iCab have en masse decidednot to follow the standard — or maybe that the standard is in fact not thestandard.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 pixelsfrom my reference point”, but it doesn’t tell us what this referencepoint 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 title="To my JavaScript Section" target="_blank">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 thatcase 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 layersalso calculate their position relative to document.If we can find the mouse coordinates relative to the document we can immediately paste them into thetop
and left
properties of the DHTML layer we wish to move, avoidingall kinds of trouble.So where can we find these mouse coordinates relative to the document?
DOM implementation’s client area
The
target="_blank">W3C DOM Level 2 Events standardspecifies two property pairs for mouse coordinates:screenX/Y
, the mousecoordinates relative to the entire computer screen, and clientX/Y
, which are definedas>“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 defineclientX/Y
as the mouse coordinatesrelative 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 thecoordinates relative to the windowclientX/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
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 mustbe 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 andthat 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 ofclientX/Y
would greatly benefitweb 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 supportclientX/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 usingclientX/Y
. Again we see that browser vendorsare 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 lastdot 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 restorethe 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. Browservendors 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
pageX/Y
is supported by Netscape 4 and 6 and sometimes by iCab. So for the benefit
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
clientX, clientY
If the browser doesn’t support pageX/Y
we have to use clientX/Y
.
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.
- If
clientX/Y
contain the coordinates relative to the document, we read them out andare ready. - If
clientX/Y
contain the coordinates relative to the window, we read them out, add the scrollingoffset 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 theydon’t. I looked at the problem from a theoretical point of view: is it possible to ascertain themeaning ofclientX/Y
by reading out some screen properties like scrolling offsetand 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 iCabcan 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
document.documentElement
instead of document.body
. I’m not yetsure of the details of this problem and in any case they are beyond the scope of thisarticle.)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, Konquerorand iCab browsers, sure. But what about the future?If any of the four browsers changes its clientX/Y
implementation
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 andnon–standard–compatible versions. And would these versions be the same on all operatingsystems?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 ofclientX/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