The other side of the moon

[philiptellis] /bb|[^b]{2}/
Never stop Grokking


Friday, August 22, 2014

jslint's suggestion will break your site: Unexpected 'in'...

I use jslint to validate my JavaScript before it goes out to production. The tool is somewhat useful, but you really have to spend some time ignoring all the false errors it flags. In some cases you can take its suggestions, while in others you can ignore them with no ill effects.

In this particular case, I came across an error, where, if you follow the suggestions, your site will break.

My code looks like this:

   if (!("performance" in window) || !window.performance) {
      return null;
   }

jslint complains saying:

Unexpected 'in'. Compare with undefined, or use the hasOwnProperty method instead.

This is very bad advice for the following reasons:

  • Comparing with undefined will throw an exception on Firefox 31 if used inside an anonymous iframe.
  • Using hasOwnProperty will cause a false negative on IE 10 because window.hasOwnProperty("performance") is false even though IE supports the performance timing object.

So, the only course of action, is to use in for this case.

Thursday, August 08, 2013

Don't guess at TimeZones in JavaScript

I spent quite some time a couple of months ago working on timezone support for mPulse and thought I should document the insanity, but never quite got around to it. Then there was this post on hacker news about reading a user's timezone in JavaScript and using that to display the right time. That post brought back a flood of horrific memories, prompting me to put my thoughts down.

First, while Trevor's post has a good hack to display a time in the user's current timezone, that hack works in only one case -- displaying the current time to the user in their device's timezone.

If you've worked with timezones and front end development for a while, this is probably the first hack you'll think up.  It turns out that in most cases, this is insufficient.

We'll first look at the problems with this approach, and then look at the requirements for proper timezone support.

Problems

  • The user's device timezone is not always correct. Some users fix their device timezone to their home time even when they're travelling, however the information you need to display may be pertinent for the location where they are right now.
  • On the other hand, the user may have their device set to automatically update timezone, but they actually want to see times in their home time (because, for example, that's when they call home, or have their calendar app configured).
  • The timezone offset (which is what you actually get from JavaScript), only tells you the offset from UTC for "right now". This information is irrelevant if you need to display a time that is not now, because daylight saving rules may come into effect.
  • You cannot use a lookup table for offset to timezone, because there isn't a one-to-one mapping between offset and timezone. It's a many-to-many mapping, and it changes.

Second attempt

A second attempt might be to figure out the timezone name by parsing the JavaScript Date.toString() output. This was my second attempt when writing the strftime function for the YUI Library.

I did this study in 2008, and it turns out that browsers are pretty inconsistent wrt Date.toString() output.

Requirements

Ok, before going into this, read this post on stackoverflow about daylight saving time and timezones.

So, what we need is the ability to do the following:

  1. store any date or range of dates.
  2. display a date in any timezone that makes sense for the user, and/or the event(s) being displayed, and/or the environment.
  3. display date ranges that may cross a timezone boundary.
  4. display a historic date in a historic timezone that may have changed due to political decisions.

The first requirement should be pretty straightforward. We'd like to store dates, and the best way is really a unix timestamp or an ISO8601 date. I prefer the latter because it takes into account leap seconds as well (unix timestamps are leap second agnostic [1],[2]). I also always use Zulu time for an ISO8601 date.

This is not sufficient, however. We also need to store the timezone name of the event. This is so that we can display historic events in the timezone they originally occurred in, even if the definition of that timezone changes. This comes from the Olson Database.

With these two pieces of information (event date/time & event timezone name), we can render the date in several ways... the original event date/time, the event date/time relative to the user's current timezone, etc.

We also need to handle date ranges. This could be something like your spring vacation, that just happened to cross several timezones because you left San Francisco on March 8th, flew to the UK, stayed there until April 7th, and then flew back. Your flight departure from SFO is in Pacific Standard Time and your arrival at LHR is in British Standard Time. Your departure from LHR is in British Daylight Time, and your arrival at SFO is in Pacific Daylight Time.

What's most important is that you display these dates in their specific timezones regardless of where the user actually is.

So should we guess or ask the user what they want?

By all means guess at what the user's timezone might be. Use a combination of GeoIP + JavaScript timezone offset to figure out where they might be (note that both of these could be wrong), but give them the option to specify the timezone that they care about.

Also, when displaying event dates, use a date local to the event, but use JavaScript to allow the user an easy way to flip it to their local timezone if they like. That's progressive enhancement.

What else shouldn't we do?

Don't try and guess the user's language or preferred currency from their current location. Always ask and store their response.

Click to help me test boomerang (opens google.com in a new window).

Monday, March 18, 2013

Reducing checkboxes

Alex Limi has an excellent post on the overuse of checkboxes in Firefox's preferences screen. It reminded me of something Nat mentioned during his talk with Miguel de Icaza back at Linux Bangalore 2003 about Gnome. They mentioned several UI idioms including checkboxes and disabled menu items, but the gist of it was, every time you give the user a decision to make, you're making their lives harder. As the domain expert for this product, it's your job to pick sane defaults and not bother the user with these choices.

We took this to heart on the Ayttm project. At the time ayttm probably had over 200 user modifiable configuration options in the preferences screen, and each plugin could add its own. It was way past the point of violating one of our primary design requirements, that it should be easy enough for Colin's mum to use. We had a bit of a dilemma though. While our target audience was definitely non technical, we had a significant number of geeky early adopters who really wanted the ability to modify everything.

Over the next few days we stripped out almost every configurable option from the Preferences screen, however, we left them all in the config file on disk. Any user that really wanted to modify the options could edit the config file in their favourite text editor and make the changes themselves. This made everyone happier. Our technical users were happy that they didn't have to click through too many screens to change all their options, and our non technical users had a preferences screen where the most they'd have to do was enter their account information, and the type of smileys they wanted.

The Gnome Human Interface Guidelines cover a lot about designing intuitive interfaces, so go read that.