Skip to page content or Skip to Accesskey List.

Work

Main Page Content

Mission Impossible Mouse Position

Rated 4.44 (Ratings: 19)

Want more?

  • More articles in code
 
Picture of ppk

Peter-Paul Koch

Member info

User since: 12 Sep 1999

Articles written: 8

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 at

the 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 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

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 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

target="_blank">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

title="My explanation of this important principle"

target="_blank">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.

The access keys for this page are: ALT (Control on a Mac) plus:

evolt.org Evolt.org is an all-volunteer resource for web developers made up of a discussion list, a browser archive, and member-submitted articles. This article is the property of its author, please do not redistribute or use elsewhere without checking with the author.