Firefox 3 .pageX / .pageY bug

Firefox 3 has a bug. It's somewhat obscure, and it probably doesn't affect you, but it did affect me.

It also affected the Microsoft Virtual Earth MapControl, so if you use the VE MapControl for a mashup, your Firefox 3 users are going to be affected by this issue.

The issue? It has to do with a mouse event's .pageX and .pageY properties. The values of those properties are supposed to report the "page" position of the mouse cursor, relative to the upper-left corner of the HTML page. You can think of it as the absolute (x,y) of the mouse cursor.

The nice thing about .pageX/Y is that serves as an easy way to get the mouse position, regardless of how much (or even if) the browser window is scrolled.

Contrast .pageX/Y with .clientX/Y: the latter reports (x,y) coordinates relative to the upper-left corner of the browser window, so these values will be affected by browser scroll position.

Anyway, at this point, you're probably thinking that I'm going to tell you that Firefox 3 reports the wrong values for .pageX/Y, and that that's the bug. It's not that simple though (again, I admit this is a somewhat obscure bug).

Most of the time, Firefox 3 reports the right values for .pageX/Y, but there is one case in which it doesn't.

Manually Dispatching Events in Javascript

A while back, I wrote a post about Re-routing events in Javascript. In it, I explain how to create an event and initialize it with myEvt.initMouseEvents(..) (which takes a boat-load of parameters).

Of all those parameters sent into .initMouseEvents(..), 4 are of particular interest: .screenX, .screenY, .clientX, and .clientY.

Notice that .pageX and .pageY are not in the list. You don't get the ability to pass in .pageX/Y when manually creating/dispatching an event in Javascipt. Presumably, the values for these properties are computed based on .clientX/Y and the browser's scroll position.

So for example, if you initialize a MouseEvent with .clientX = 200 and .clientY = 300, and if the browser is scrolled 50 x 75, then your event's .pageX and .pageY values will be 250 and 375 respectively.

Oops! I mean, that's what the values should be.

The Bug

Firefox 2 actually gets it right, but Firefox 3 doesn't.

When you're manually dispatching a MouseEvent in Firefox 3, the .pageX/Y property values will always be equal to the .clientX/Y values, regardless of browser scroll position.

As I previously mentioned, this affects the VE MapControl, so it's easy to see the issue in action. Just use Firefox 3 and point your browser to http://maps.live.com. Now make your browser window small enough to cause scroll bars to appear. Scroll the browser window by some amount (doesn't matter how much) and try panning the map. You should see the map "jump" during the initial pan. The amount it "jumps" is going to be equal to the amount by which the browser window is scrolled.

The Fix

Microsoft has a work-around for this issue that is going to be released with the next version of Virtual Earth, but if you're a mashup dev using the VE MapControl, I've got a solution that you can use right now, and it goes like this:

(function()
{
    var mouseEvt;
    if (typeof document.createEvent !== 'undefined')
    {
        mouseEvt = document.createEvent('MouseEvents');
    }
    if (mouseEvt && mouseEvt.__proto__ && mouseEvt.__proto__.__defineGetter__)
    {
        mouseEvt.__proto__.__defineGetter__('pageX', function()
        {
            return this.clientX + window.pageXOffset;
        });
        mouseEvt.__proto__.__defineGetter__('pageY', function()
        {
            return this.clientY + window.pageYOffset;
        });
    }
})();

Just include the preceding code snippet anywhere in your HTML page. The code is pretty straight-forward and just relies on the fact that Firefox (and others) have the ability to define getters through the use of .__defineGetter__(..).

The .pageX/Y properties are read only, but it turns out that the browser will let you redefine these getters, thereby overriding their return logic. Since the .clientX/Y properties appear to always report the correct value, all we have to do redefine the .pageX/Y getters to use a combination of .clientX/Y + .pageX/YOffset.

In other words, we've redefined .pageX/Y to always return the position of the event (in our case, the mouse cursor since we're dealing with MouseEvents), relative to the upper-left corner of the browser window + the scrolled position of the browser -- what it should have been all along!

This code doesn't hurt IE, as it won't execute in that browser, and it doesn't hurt Firefox 2 or other browsers that understand the code either, because the logic we've defined for the getters is going to be essentially the same as what the browser would have done natively anyway.

I'd like to file a bug to Mozilla on this issue, but I haven't a clue as to how one goes about doing that. If anyone knows (or wants to do it for me), feel free to speak up!

Enjoy the fix!

Comments welcome.

12 Responses

  1. RichB Says:

    So you haven't been testing with Firefox 3 betas/RCs? Shame.
    Your bug looks like it has been reported here:

    https://bugzilla.mozilla.org/show_bug.cgi?id=411031

    If you vote on it getting fixed, you'll be added to the email list for it. It's quite obscure so may not have high priority – you could try submitting a patch yourself?

  2. sstchur Says:

    RichB:

    Thanks for the link. Out of curiosity, how did you find it? I searched Buzilla for "Firefox 3 pageX" and a number of other search terms, but I got back so many results that I couldn't possibly sort through to find what I really wanted.

    What is the secret to finding stuff in Buzilla?

    I was using FF3 RCs for a while, personally, but I think this one slipped through the cracks for VE, just because it doesn't come up that often. But yeah, you're right — we should have been more vigilant with FF3 testing.

  3. Dan Fabulich Says:

    This bug doesn't say "pageX" in its summary, so a simple search won't turn it up. It will turn up on a search that includes "pageX" in comments. http://tinyurl.com/5zsa23

  4. sstchur Says:

    Ah, thanks Dan!

    I see that there has been some chatter on the bug. I've added my comments and voted in hopes of getting it fixed.

  5. Dan Fabulich Says:

    Please also follow up with your comments on bug 405632. One Mozilla dev (Smaug) said this on the matter: "IMO http://blog.stchur.com/2008/07/03/firefox-3-pagex-pagey-bug/ describes a feature not a bug. Dispatching an event manually shouldn't be affected by the layout of the page. (Unfortunately pageX/Y and layerX/Y aren't defined in DOM Events specification)"

  6. Jon Hudson Says:

    I have tried for days to fix this issue for my mapping mashup. Very well done sir! Thank you.

  7. sstchur Says:

    Glad it helped Jon!

  8. csavage Says:

    I must say, your fix worked perfectly.
    Thanks for documenting the issue!

  9. Mike Says:

    Thanks for this.

  10. Gubmimikall Says:

    The good resource is informative and actual

  11. eric Says:

    You just saved me hours worth of work. I spent two hours last ripping through zindexes and float positions trying to figure out why my VE controls were acting this way in FireFox 3 (Chrome and IE7 seem fine, as you noted). Thanks so much. I never would have cleared this otherwise.

  12. Stephen Stchur Says:

    @eric:

    Glad it helped you out! FYI, the latest version of the VE MapControl should have the fix for this built in.

Got something to say?

Please note: Constructive criticism is welcome. Rude or vulgar comments however, are not and will be removed during moderation.