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.”
Yehuda Katz (March 21, 2008 at 1:04 am)
Just to be even clearer:
Non-text keypresses have two life-cycle events: keydown and keyup
Text keypresses have an additional life-cycle event: keypress
You should be able to completely ignore the keypress event if there is no textual data involved with little-to-no side effect.
Jed Schmidt (March 21, 2008 at 1:53 am)
There’s actually one case in which keypress is (was?) useful for non-text keys: key repeating. If you hold down an arrow key, for example, the keypress event should get fired repeatedly until the key is released, after a short delay based on OS keyboard settings.
I suppose you could hack around this using setTimeout to create a custom setInterval to be removed on keyup, but it would be nice to be able to defer to the user’s accessibility settings for repeat speed.
John Resig (March 21, 2008 at 2:13 am)
@Jed: According to the WebKit guys the keydown event repeats for non-text keys, as well. It doesn’t really seem like the keypress event was terribly useful, in this case (previously).
Dean Edwards (March 21, 2008 at 8:30 am)
The keypress event is not standard. The W3C defines a textInput event instead.
http://www.w3.org/TR/2002/WD-DOM-Level-3-Events-20020208/events#Events-TextEvents-Interfaces
Keydown is also different to keypress in that the event can be cancelled using preventDefault(). That’s an important advantage of keydown.
It’s also worth mentioning that the keyboard event model that was used by Safari before version 3.1 is shared by Opera, FF on Mac, Konqueror and FF on Linux.
I’ve standardised keyboard behavior in the next release of base2.DOM by firing the missing events on those platforms.
http://code.google.com/p/base2/source/browse/trunk/src/base2/DOM/events/EventTarget.js
Jason Tackaberry (March 21, 2008 at 7:35 pm)
@John: I’ve observed the same thing as Jed, which is why I use keypress. At least for Firefox (including the nightlies), keydown does not repeat. I have this bit of code from a recent project:
// Firefox doesn't fire multiple keydown events when keys are held,
// whereas IE does. But IE doesn't fire keypress events for arrow
// keys. So we use 'keydown' for IE, and 'keypress' otherwise.
var key_event = $.browser.msie ? 'keydown' : 'keypress';
Maciej Stachowiak (March 23, 2008 at 6:54 pm)
We should have announced this change more widely ahead of time; in retrospect the mailing list post was not enough. We’ll try to broadcast future changes more widely.
In general, we hope that the key event situation is improved over time and that different browsers become more interoperable.
Diego Perini (March 23, 2008 at 8:09 pm)
IE seems to have a somehow related “event.repeat” property.
http://msdn2.microsoft.com/en-us/library/ms534367(VS.85).aspx
They say:
“Retrieves whether the onkeydown event is being repeated.”
But I don’t know if it may be of use…never used.
Probably re-firing the event if that property is “true” ?
Diego Perini
Bahr-El-Hawa (March 27, 2008 at 5:51 am)
According to the WebKit guys the keydown event repeats for non-text keys, as well. It doesn’t really seem like the keypress event was terribly useful, in this case (previously).
Joaquin Zuazo (May 17, 2008 at 12:18 pm)
I am working with firefox 2.0.0.14 with Linux/Fedora 7. I don’t understand the keyboard events characters, when accents for spanish or another europeans languages are used. When you get an á,ä,â or à and similar, only the keyup event is fired, for two times. The first it shows a keycode of 0, and the next a keycode of 65, an ascii A. Not keypress or keydwons events at all. Also, a repeated accent does not fire any event. With Firefox on windows it looks to work right.
Can you help me on this subject?
Thanks