There was an interesting post the other day which criticized Safari’s choice to completely overhaul their event system and, in the process, seemingly cripple the keypress event for non-character keys.

At first glance to me, and to many others, it appeared as if a dramatic flaw had been introduced into Safari’s event system – completely castrating its practical use. It was at this point that I contacted Yehuda Katz (Merb and jQuery contributor) – he knows more about the inanity of cross-browser keyboard events than anyone I know. Here’s his responses to a few questions that I bounced off him:

John: At first glance through WebKit’s changes it appears as if they’ve significantly crippled the keypress event, is this the case?

Yehuda: People should not have been using the keypress event to get the character that was pressed. That’s because the keydown event only provides information about the actual key that was pressed (the A key, the right arrow, etc.), but does not tell the user what character will get added to (for instance) an input box.

Since arrow keys do not get added as text, keydown provides all the information that is needed. There were a couple of quirks with using keydown in certain cases previously which are resolved by their changes that prevent keypress from getting called if keydown’s default handling is blocked.

What this means is that if people were using keypress before (and relying on Safari’s native results for arrow keys, such as 63232), their code will break. However, this was a bad idea all along; people should use keydown to detect and block non-character keys. My mantra has always been: keydown/keyup if you want to know the key that was pressed; keypress if you want to know what text was detected.

John: Being able to access non-character keys must be useful – granted Safari gave strange results previously – but at least some value is better than none, right?

Yehuda: The keypress event does not provide any additional benefits over keydown for these non-character keys. Keep in mind that IE does not fire the keypress event for some non-character keys (e.g. delete, end, enter, escape, fkeys, etc.).

Because Safari appears to have fully resolved issues with suppressing default events from keydown, there should be no problem with using keydown for non-character keys.

John: So we have charCode, keyIdentifier, and keyCode along with keyup, keydown, and keypress – is there a solution that we can safely use in a cross-browser manner?

Yehuda: There’s a reasonably good writeup at: http://unixpapa.com/js/key.html.

Effectively, you can use e.keyCode for all modern browsers in keydown/keyup (although you’ll need to consult the chart at the bottom of that page to determine whether the keyCodes returned by the browsers are consistent).

For keypress, e.charCode || e.keyCode should work (Mozilla and Safari define e.charCode, while IE defines e.keyCode which returns the ASCII value). What that means is that you have a close-to-cross-browser keyCode for keyup/keydown and a pretty reasonable charCode for keypress.

As a result, keypress can be used for key events that produce text for which you need to know its ASCII value (A vs. a). keyup/keydown can be used for events for which you care about the physical key that was pressed. Again, there are some quirks, which can be summed up in the tables at http://unixpapa.com/js/key.html.

I’d like to thank Yehuda for taking the time to clear these points up. Effectively if you were doing key-related development, previously, in a non-cross-browser manner then you hit a wall with this recent change. If nothing else this should encourage you to keep a good separation, as Yehuda notes: “keydown/keyup if you want to know the key that was pressed; keypress if you want to know what text was detected.”