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.

CSS Word-wrap: break word; (revisited)

HedgerWow is a great blog. If you've never checked it out, you definitely should.

I just noticed the other day that his latest post gives some props to my Emulating CSS word-wrap for Mozilla/Firefox blog post, but his version adds some much needed cross-browser support for browsers I neglected.

Nice One, HedgerWow!

Palindromes

The other evening while I was watching TV, I happened to have my laptop out, so I started dabbling around in Firebug just for the heck of it. I'll often just start writing functions for no particular reason, other than to refresh my skills or perhaps discover something about Javascript that I hadn't previously known.

So I was monkeying around, and I decided to write a function to determine if a string were a palindrome. This is not a very difficult problem, but it might make for a descent introductory interview question (one of those questions you'd ask to rule out the truly inept).

With problems like this, I'm mostly interested in two things:

  1. Writing the function in a clever way, so as to achieve the least amount of code possible
  2. Writing the function in the most efficient way possible

Now, if it were Ruby, writing the clever solution (which might also happen to be the most efficient solution in that language -- not sure) would be ridiculously easy:

def pal(s)
   s == s.reverse
end

That's so trivial, it's probably not even worth writing.

But Javascript doesn't have a .reverse() method on strings, so you have to take an extra step (still pretty easy though):

function pal(s)
{
   return s === s.split('').reverse().join('');
}

Split the string into an array, reverse the array, and then join it back together. Since this is all native Javascript stuff, it turns out to be reasonably fast, though one might expect you could do better.

For grins and giggles, I decided to see how a manual solution, where I compare characters starting at each end of the string and work towards the middle, would compare:

function pal(s)
{
   var i, j,
   len = s.length,
   mid = Math.floor(len / 2);

   for (i = 0, j = len - 1; i < mid; i++, j--)
   {
      if (s.charAt(i) !== s.charAt(j)) return false;
   }
   return true;
}

This function will probably fail quickly, so that's good, but it also turns out to be generally faster than the split/reverse/join version. It makes sense, since you're going through the string only one time and twice as fast as normal since you're traversing from both ends at the same time.

Can you do even better though? Maybe. At least in Firefox you can, and quick tests seemed to confirm across other browsers too (but I didn't test extensively).

What if we split the string in half, and only reverse half of it. Then we can compare the first half with the original half:

function pal(s)
{
   var len = s.length,
   x = len / 2,
   y = x === Math.floor(x) ? x : (x = Math.floor(x)) + 1;

   return s.substr(0, x).split('').reverse().join('') === s.substr(y,len);
}

The idea here is just split the string in half, reverse one of the two pieces and compare them. For example:

TOOT: splits into "TO" and "OT." Reverse one of them, say the second part ("OT") and compare.

If you've got a odd number of characters, you can ignore the middle most character:

RACECAR: splits into "RAC" and "CAR" (ignore the "E" in the middle). Reverse the second part ("CAR") and compare.

As it turned out, this seemed to be faster than the other two methods. Not by orders of magnitude, but not insignificantly either. Here are the number from Firefox, running each function 10,000 time on the string "gohangasalamiimalasagnahog" (go hang a salami, i'm a lasagna hog).

  • (Traverse from both ends): 797ms
  • (Split/Reverse/Join): 969ms
  • (Compare halves): 640ms

Of course, numbers varied slightly during each run, but overall the they were consistent relative to each other.

So there ya go. Nothing in particular I wanted to point out here; just found it interesting and thought you might too.

Think my code sucks? Got a better solution? Say so in the comments (you won't hurt my feelings).

Programmatically Accessing the CSS Rules in Your Page's Stylesheets

It's not a scenario that comes up too often, but every once in a while, I find the need to programmatically access a CSS rule in one of my stylesheets (which includes rules embedded in the page via the <style> tag).

The major problem here is that there is only one mechanism available for accessing a CSSStyleSheetDeclaration object and its corresponding CSSRule collection.

And that (rather unfortunate) mechanism is by zero-based index, which means you need to know exactly where in the DOM a particular stylesheet lives and exactly where in said stylesheet the particular rule you're interested in lives.

Needless to say, accessing these sheets and rules by index is risky, since any update to your CSS may require an update to the code which accesses your CSSRule by index.

You might be thinking that one solution is to put the rule (or rules) that interest you in a predictable place (e.g. the very first rule of the very first stylesheet in your page), but even this is risky and error prone (I know; I did it, and it once came back to bite me).

In light of these issues, I wrote a function that, given a CSS selector rule, finds the the corresponding CSSRule (if it exists). Since this is not necessarily a cheap process, the results are cached so that future requests for the same rule are returned in constant time.

The full code is offered below, with explanations of anything interested embedded in comments.

The findCSSRule(..) function

var findCSSRule = function()
{  
   var cache = {};
   function worker(_selector, _media)
   {
      if (typeof cache[_selector] !== 'undefined')
      {
         return cache[_selector];      // return the cached rule if it exists
      }
      
      _media = _media || '';            // if _media isn't passed in, default to empty string
      
      var i, j,
      mediaText,
      sheet, rule, rules, rulesLen,
      sheets = document.styleSheets,
      sheetsLen = sheets.length;
      
      for (i = 0; i < sheetsLen; i++)
      {
         sheet = sheets[i];
         
         // accessing the media attribute of the stylesheet requires a bit of cross-browser trickery
         mediaText = typeof sheet.media.mediaText !== 'undefined' ? sheet.media.mediaText : sheet.media;
   
         // don't waste time with this sheet if its not of the right media type
         if (mediaText.indexOf(_media) === -1)
         {
            continue;
         }
         
         rules = sheet[prop];
         rulesLen = rules.length;
         for (j = 0; j < rulesLen; j++)
         {
            rule = rules[j];
            if (rule.selectorText === _selector)
            {
               return cache[_selector] = rule;
            }
         }
      }

      return null;        
   }

   // this function is returned if the browser doesn't provide a known
   // mechanism for accessing stylesheets and/or CSS rules
   function unsupported()
   {
      return null;
   }

   // make sure there's at least 1 stylesheet to search,
   // then prop will be "cssRules" (W3C), "rules" (IE), or null (unsupported)
   var test = document.styleSheets.length > 0 && document.styleSheets[0];
   var prop = test &&
      (typeof test.cssRules !== 'undefined' ? 'cssRules' :
      typeof test.rules !== 'undefined' ? 'rules' :
      null);

   // false prop (meaning no stylesheets or unsupported method of accessing the CSSRules),
   // means this function won't work, so rather than return the worker, return the unsupported function
   return prop ? worker : unsupported;
}();

Caveats:

This function isn't perfect by any means -- a few things need to be called out.

  1. Specifying media type could be more flexible: I'm just doing a simple indexOf(..) check on the stylesheet's media attribute, which means that if you want to filter by more than one media type, you'll need to specify the comma separated list yourself, and you'll need to make sure you do it in the right order if you want the indexOf(..) check to pass. A more robust solution might be to parse a comma separated string of media types for the user, or to allow the user to specify multiple media types in an array.
  2. Memory consumption could be an issue: I don't think it's likely, but if you're grabbing lots and lots of rules, the cache could become large and memory leaks could become an issue (since the cache is created in a closure). I'm not overly concerned about this though -- you can decide for yourself is this is a big deal.
  3. No mechanism for handling dynamically created styles: If you happen to load a stylesheet later in page's lifetime (after the initial load) this function is still going to return the unsupported() function, since it does its check once, up front, to decide whether or not accessing stylesheets/rules is feasible. I'm okay with this, but again, you can decide for yourself.
  4. Examples:

    Now that the function is written, a few examples are in order. Assume the following stylesheets:

    - stylesheet 0 - (media = "print")

    div>span
    {
       border: 10px solid red;
    }

    .christmas
    {
       background: green;
       color: red;
    }

    .pascha
    {
       background: purple;
       color: yellow;
    }

    - stylesheet 1 - (media = "screen")

    div>span
    {
       border: 5px dotted blue;
    }

    #main div~span
    {
       background: pink;
    }

    Accessing any of the above styles is fairly trivial.

    // returns the div>span rule from stylesheet 0
    var r1 = findCSSRule('div>span', 'print');
       
    // also returns the div>span rule from stylesheet 0 (since media default to empty string, the first stylesheet with this rule will match)
    var r2 = findCSSRule('div>span');
       
    // returns the div>span rule from stylesheet 1
    var r3 = findCSSRule('div>span', 'scren');
       
    // returns the .pascha rule from stylesheet 0
    var r4 = findCSSRule('.pascha');
       
    // returns null
    var r5 = findCSSRule('.halloween');
       
    // returns the #main div~span rule from stylesheet 1
    var r6 = findCSSRule('#main div~span');
       
    // returns null (b/c the rule doesn't exist in a stylesheet with media = "print"
    var r7 = findCSSRule('#main div~span', 'print');

    And there you have it!

    Comments, questions, suggestions, complaints? Bring 'em on!

Fresh Nuggets of Gimme Awesomeness

Some really cool stuff happening with Gimme lately, especially with regards to its animation capabilities.

I whipped together something that started off (in my head anyway) as a sort of copy of CoverFlow on Mac OS. However, before I was finished, it had morphed into a general purpose animation-along-an-arbitrary-path-type-routine.

This code isn't yet checked into the repository, but for anyone who is interested in taking a look, here are two different demo pages to see the "bleeding edge" of what is going on with Gimme:

There are a few things worth nothing. The first link is a really quick-n-dirty prototype, so there are bound to be bugs. The page takes a few seconds to load as it is pulling remote photos from my Spaces photo album. You'll have to wait until everything is loaded and in place to try out the demo. Just click on any image and you'll get the idea. You can also change the shape of the curve that the images follow by clicking on the "S Shape" button.

The second link is a general animation demo page. By providing a set of Bezier control points (comma separated coordinates / spaces separated points), you can instruct the script to plot any curve and then have the red block follow that curve. You can also chose the AccelerationLine you want to use with the animation (note that Linear is a bad name, and should really be None -- I will fix this one day).

You can even use the text area on the right side of the page to execute arbitrary custom code. I've provided a sample that animates the red block counter-clockwise around an ellipse 2 and 1/2 times.

As you can see, it doesn't take much code at all to achieve some fairly sophisticated animation with Gimme.

In a future blog entry, I hope to polish up this code and provide a deeper explanation of the samples (as well as get this code checked into the repository).

Comments welcome

IE quirk with onload event for <img> elements

I rediscovered something yesterday that I know I've come across in the past. Still, it was driving me crazy until I finally did a little web searching and found what I needed. It was one of those "oh right! I've encountered that before" moments.

I was writing a script that dealt with images and I needed to know when an image had finished loading. Simple enough. Using Gimme (or your favorite addEvent(..)) equalizer the code looks something like:

var myImage = document.createElement('img');
myImage.src = 'http://source.to.image/image.jpg';
g(myImage).addEvent('load', function(e) { alert('Image is done loading!'); });

It turns out that my load handler wasn't always firing in IE (in Firefox it worked consistently). The solution though is super simple.

You just need to make sure that you wire up the load handler before setting the image element's .src property. Why? Because if the image is being loaded from cache, IE (and Opera too) will load the image instantly. In fact, it will have finished loading the image before your load event handler is even wired up, which means, it won't fire!

Just change the code to something like this:

var myImage = document.createElement('img');
myImage.addEvent('load', function(e) { alert('Image is done loading!'); });
myImage.src = 'http://source.to.image/image.jpg';

And there you have it.

Nothing earth-shattering and probably common knowledge for a lot of scripters, but a little refresher never hurts.

Anna Stchur

Anna Stchur entered the world at 11:44pm on Tuesday, February 5th 2008 at 6lbs 13oz.

Anna Stchur, just minutes old

Already, my absolute favorite thing in the whole world.

Style anything in IE

A lot of people have been writing about this recently, and I can't take credit for discovering it. I first saw it here and then again here, but I'll recap it briefly on this blog for anyone who is too lazy to follow those links.

Under normal circumstances, IE won't allow you style tags that it doesn't recognize. But there is an interesting quirk in IE that will allow you to work around this and style unknown elements. All you have to do is create an element whose tag name is that which you desire to style, like so:

document.createElement('myNewTag');
Note that there is no need to actually add the newly created element to the DOM, or even store it in a variable. Just the process of "creating" the element is enough to "wake IE up" to the fact that such a tag exists.

With the Javascript in place, you could then do:


<style type = "text/css">
myNewTag
{
   border: 5px solid #f00;
   background: #00f;
   font-weight: bold;
}
</style>
<myNewTag>Brand new tag that has never existed before!</myNewTag>

Now IE will happily style all <myNewTag> elements with a red border, a blue background and bold text.

This goes for any tag that IE would not normally recognize, but it may prove especially useful in handling HTML5 down the road.

So there ya go... another IE quirk "to the rescue."