The tests were done with the intention of learning just enough to write the code I needed to write. They are not comprehensive or authoritative and do not cover all aspects of keyboard event handling. It is based on a limited number of tests on a limited number of browsers. The results cannot necessarily be extended to other versions of these browsers. The browser versions most recently tested are:
The references in this document to iCab all refer to iCab version 3. iCab version 4 uses the same layout engine as Safari version 3, and generally behaves identically. Probably we will eventually cut out all discussion of iCab, since supporting obsolete versions of iCab shouldn't be a priority for any programmer.
The script used to collect the test results reported here is available at http://unixpapa.com/js/testkey.html.
In every browser tested, there were three events triggered when a key was pressed and released:
keydown
keypress
keyup
The keydown event occurs when the key is pressed, followed immediately by the keypress event. Then the keyup event is generated when the key is released.
To understand the difference between keydown and keypress, it is useful to distinguish between "characters" and "keys". A "key" is a physical button on the computer's keyboard. A "character" is a symbol typed by pressing a button. On a US keyboard, hitting the "4" key while holding down the "SHIFT" key typically produces a "dollar sign" character. This is not necessarily the case on every keyboard in the world. In theory, the keydown and keyup events represent keys being pressed or released, while the keypress event represents a character being typed. In practice, this is not always the way it is implemented.
Note that DOM3 defines only the keydown and keyup events, not keypress. Instead of keypress it defines a new event called textinput, which is sort of a generalization of keypress which is supposed to fire whenever text is input, whether by keyboard or not (it could be spoken text or cut/pasted text). So far as I know, no browser yet supports textinput.
"Modifier keys" are keys like [Shift], [Control] and [Alt], that don't send characters, but modify the characters sent by other keys. For most browsers, both keydown and keyup events are triggered by modifier keys, but keypress events are not. This is consistant with their being "key" events not "character" events. However, there is a lot of variation:
Browser Events sent when modifier keys are typed Gecko
Internet Explorer
Safari 3
keydown
keyupOpera
Konquerorkeydown
keypress
keyupSafari 2
iCab 3no events sent
Another special class of keys are the four arrow keys. There are no standard ASCII codes for the characters sent by these keys, but most browsers treat them as regular keys, triggering all three events. However, in Internet Explorer and Safari 3 they seem to be classified with the modifier keys, and generate only keyup and keydown events.
If a key is held down long enough it typically auto-repeats. (Note that modifier keys never auto-repeat.) In all browsers, each auto-repeat triggers more events. In most browsers, an autorepeat is sensibly treated as a character event, but not a key event, so it triggers a keypress but not a keydown or keyup. But, of course, there is some variation:
Browser Events triggered on each autoreapeat Gecko (Linux)
Gecko (Macintosh)
Safari 2
Konqueror
Opera
keypress only Internet Explorer
Gecko (Windows)
Safari 3keydown
keypressiCab 3 keyup
keydown
keypress
Arrow keys do autorepeat in IE and Safari 3, but only keydown events are generated, not keypress events.
If you are installing your own handlers for key events, then sometimes you won't want the browser default action to occur (such as having the character appear in a text entry area). To prevent this, you typically have the event handler return false, and maybe call event.preventDefault() and event.stopPropagation() if they are defined. But on which event handler must you suppress defaults? This, of course, varies from browser to browser.
Some older versions of Gecko may have suppressed defaults only on keypress, but I'm not sure of that at this point.
Browser Which event handlers need to suppress defaults to prevent key from appearing in text box Opera
Konquerorkeypress Internet Explorer
Gecko
Safarieither keydown or keypress iCab 3 keydown
Suppressing defaults on the keydown event has some odd side effects on some browsers, in that it may prevent some other events from firing. Apparantly, triggering further events is taken to be part of the default action of the keydown event in these browsers.
Most applications will either use only keypress or use only keyup/keydown, so this all works out pretty well in most browsers. If you are handling keypress and want to suppress default handling of the key, return false from that handler. If you are handling keydown/keyup and want to suppress defaults, install a keypress handler that does nothing except return false.
Browser Side effect if keydown handler returns false Gecko
Safari 2
OperaNo change Internet Explorer
Safari 3keypress event never occurs.
keyup event works normally.Konqueror keypress event only occurs on auto repeats.
keyup event works normally.iCab 3 keypress event never occurs.
keyup event never occurs.
For iCab 3, we have problems. If you want to suppress default handling of the key, you need the keydown handler to return false. But if you do that, both keypress and keyup events disappear. So you can't do either keypress handling or keyup.
The DOM3 standards say that keyup should still occur if the default action on keydown is suppressed, but textinput should not.
To give a clearer side by side comparison, suppose we press the [Shift] key, then press the [A] key, holding it down long enough to auto-repeat just once, then release [A], and the release [Shift]. The events we see are shown below for various browsers. Events marked in red do not occur if there is a keydown handler that returns false.
The only two browsers who behave identically are Internet Explorer and Safari version 3. This is entirely due to major effort that was made on the part of the Apple WebKit developers to revise the handling of key events in Safari to agree with IE. Kudos.
Internet Explorer
Safari 3Gecko
(Windows)Gecko
(Linux/Mac)Safari 2 Opera Konqueror iCab 3 [Shift] pressed keydown
keydown
keydown
keydown
keypresskeydown
keypress[A] pressed keydown
keypresskeydown
keypresskeydown
keypresskeydown
keypresskeydown
keypresskeydown
keypresskeydown
keypress[A] autorepeats
keydown
keypress
keydown
keypress
keypress
keypress
keypress
keypresskeyup
keydown
keypress[A] released keyup keyup keyup keyup keyup keyup keyup [Shift] released keyup keyup keyup keyup keyup
However, in my opinion, it is the Linux/Macintosh versions of Firefox and other Gecko browsers get the prize for making the most sense. It'll be a minor pity if IE turns becomes the standard.
If keypress is a character event, then it is pretty clear that it should return the ASCII code of the character typed, and it generally does.
But keydown and keyup should return a code identifying a key, not a code identifying a character. It is not obvious how to do this. ASCII codes don't really suffice, since the same key can generate different characters (if combined with shift or control), and the same character can be generated by different keys (such as the numbers on the keyboard and the numbers on the keypad). Different browsers use different ways of assigning numeric codes to the different keys. We'll call these "Mozilla keycodes", "IE keycodes", "Opera keycodes" and "psuedo-ASCII codes" and we'll explain them in more detail below.
The browsers also differ in where they return these values. Three different properties of the event object may be used to return them. They are event.keyCode, event.which and event.charCode.
Now for the actual values being returned for different keys. Some people refer to the Mozilla/IE keycodes as "scan codes". Scan codes are returned from the keyboard, and are converted to ASCII by the keyboard drivers. They typically vary with different kinds of keyboards. As far as I can tell, these are NOT scan codes. They vary with the browser type rather more than with keyboard type, and they don't seem to match with any keyboard scan codes that I've seen documented. I have only tested US keyboards though. It's likely the codes also are different on non-US keyboards.
event.keyCode event.which event.charCode Internet Explorer keydown/keyup: IE keycode keypress: ASCII code
undefined undefined Gecko keydown/keyup: Mozilla keycode keypress: zero
keydown/keyup: Mozilla keycode keypress: ASCII code
keydown/keyup: zero keypress: ASCII code
Safari keydown/keyup: IE keycode keypress: ASCII code
same as event.keyCode ASCII code Opera (Windows) keydown/keyup: Opera keycode keypress: ASCII code
same as event.keyCode undefined Opera (Macintosh/Linux) keydown/keyup: Pseudo-ASCII code keypress: ASCII code
same as event.keyCode undefined Konqueror keydown/keyup: Pseudo-ASCII code keypress: ASCII code
same as event.keyCode keydown/keyup: zero keypress: ASCII code
iCab 3 keydown/keyup: Pseudo-ASCII code keypress: ASCII code
same as event.keyCode ASCII code
The table below lists values for a few of the more common keys on US keyboards.
Note that all four encodings agree on most of the common keys, the ones hilighted in green in this table. For the letters and numbers and for spaces, tabs, enters, and arrows the codes are all the same. In fact, they are all standard ASCII values (except for the arrows). However, for symbols (which generally share a key with a number or another symbol) chaos reigns.
Key Mozilla keycodes IE keycodes Opera keycodes pseudo ASCII codes Alphabetic keys ASCII code of uppercase version of the letter even if shift key was not held down [ Space ] 32 32 32 32 [ Enter ] 13 13 13 13 [ Tab ] 9 9 9 9 [ Backspace ] 8 8 8 8 [ Shift ] 16 16 16 16 [ Control ] 17 17 17 17 [ Alt ] 18 18 18 18 left arrow 37 37 37 37 up arrow 38 38 38 38 right arrow 39 39 39 39 down arrow 40 40 40 40 Keyboard number keys ASCII code of number (48 plus number), even if modifiers are held down. Thus [ 2 @ ] key is 50 (ASCII '2') whether or not shifted. ASCII code of character that will be sent. Thus [ 2 @ ] key is 50 (ASCII '2') if unshifted and 64 (ASCII '@') if shifted. [ ; : ] 59 186 59 59 (unshifted) or 58 (shifted) [ = + ] 61 187 61 61 (unshifted) or 43 (shifted) [ , < ] 188 188 44 44 (unshifted) or 60 (shifted) [ - _ ] 109 189 45 45 (unshifted) or 95 (shifted) [ . > ] 190 190 46 46 (unshifted) or 62 (shifted) [ / ? ] 191 191 47 47 (unshifted) or 63 (shifted) [ ` ~ ] 192 192 126 126 (unshifted) or 96 (shifted) [ \ | ] 220 220 92 92 (unshifted) or 124 (shifted) [ ' " ] 222 222 39 39 (unshifted) or 34 (shifted) Keypad number keys 96 plus number 48 plus number (ASCII code of number)
The Opera keycodes have a certain simple charm. They are always the ASCII code of the character that the key sends when it is not modified by shift or control. They don't allow you to distinguish numbers typed on the keypad from numbers typed on the keyboard, and such like things, but they are, at least, fairly intuitive. Where the Mozilla and IE people got their keycode values for symbol keys is a deep mystery.
The pseudo ASCII codes aren't really key codes at all. They are just the ASCII code for the character except that for lower case characters the upper case ASCII code is sent. So these browsers have really entirely abandoned the idea of keycodes, instead returning character codes slightly modified for partial IE compatibility. There is much to be said for abandoning key codes, since the concept really gets you in trouble as you try to handle international keyboards, but something is lost when you do that. You can't, for example, tell if a number was typed on the main keyboard or the keypad. I prefer Safari's approach, where they keep the keycodes (making them entirely compatible with IE keycodes) but also return the character code on all key events.
Opera and Konqueror's use of ASCII-based codes causes another problem on keydown and keyup events: you can't always recognize the arrow keys. These browsers send the same codes as IE does for arrow keys: the values 37, 38, 39, and 40. These happen to be the ASCII codes for "%", "&", "'" and "(". In the keycode schemes this isn't a problem, because those characters are all produced by shifted keys, so those codes would never be produced as a keycode. But when pseudo-ASCII keycodes are used, as in Konqueror and some versions of Opera, these same values are also sent when you type those keys, so you can't tell those symbols from arrow keys.
The problem of telling the difference between arrow keys and various symbols also occurs on keypress events. Since there aren't really ASCII codes for the arrow keys, it's not entirely clear what should be returned. Gecko, Opera, and Konqueror return character codes of 37, 38, 39 and 40 for the arrow keys, just as they do for "%", "&", "'" and "(". IE and Safari 3 avoid the problem by not having keypress events for arrow keys. Only Safari 2 and iCab 3 handle the problem of finding ASCII codes for arrow keys sensibly, by inventing unique values. However, they return different values:
I expect that the Safari 2 values were derived from Unicode values, but haven't checked.
Browser "ASCII" values sent for arrow keys up arrow down arrow left arrow right arrow Gecko
Opera
Konqueror38 40 37 39 Safari 2 63232 63233 63234 63235 iCab 3 30 31 28 29
To complete the thoroughness of the mess, keycode generation in current Macintosh versions of Gecko is seriously buggy. For many keys, no key codes are returned on keydown and keyup events. Instead the keyCode value is just zero, Characters that give a zero keycode when typed include those listed below, as well as any key when the Alt/Option key is held down.
- _ ~ ! @ # $ % ^ & * ( ) + | : < > ?Macintosh Gecko does give correct charCode values on keypress events, but to a keydown or keyup handler, the above keys are all indistinguishable. This bug was reported to Mozilla in (bug 44259) in June 2000, and fixes have been written but, as of May 2008, these fixes have not made their way into any released browser. Eight years later, the bug still exists in the beta 5 release of Firefox 3.0.
Weirdly, iCab 3 also sets keyCode to zero for the same characters. However, charCode has a usable value in these cases, so it's less of a problem than in Gecko. This doesn't seem to be a general Macintosh problem. Safari and Macintosh versions of Opera work fine.
The DOM3 standard abandons all hope of creating order among event.keyCode, event.which and event.charCode, and instead defines new values for keydown and keyup events, event.keyIdentifier and event.keyLocation. The keyIdentifier is a string that in most cases looks like "U+0041" where the "0041" part is the unicode value of the character sent by the key, in this case the letter "A". For keys that don't send unicode characters, or where the unicode value is not standardized, it is a string like "Enter", "Shift", "Left" or "F9". A complete list is here. The keyLocation value gives values to distinguish among multiple keys that have the same identifier, like the left and right shift keys, or the keypad number keys.
Safari supports keyIdentifier and gets it right. Konqueror returns values like "Shift" and "Enter" correctly, but instead of returning the Unicode values, it returns the typed character itself, "a" or "A" instead of "U+0041". No other browsers tested support this at all yet.
The keypress events are generally the easiest to work with. Except for some confusion with arrow keys, it's usually easy to identify which key was pressed. You can get the character typed by doing:
String.fromCharCode(event.charCode ? event.charCode : event.keyCode);
For keydown and keyup events, you can identify most common keys (letters, numbers, and a few others) by just looking at the event.keyCode and more or less pretending that it is an ASCII code. However, it isn't really, and the many Javascript manuals that say it can be converted to a character by doing "String.fromCharCode(event.keyCode)" are wrong. On keydown and keyup events, the key codes are not character codes, and this conversion will give wild results for many keys. There is no general portable way to convert keycodes to characters. You pretty much have to sense the browser type and base the key mapping on that.
Because of bugs, many keys cannot be distinguished on keydown and keyup in Macintosh Gecko.
Hope for sanity exists, with DOM3, but it has not yet arrived.