不同浏览器对键盘事件的不同认知


原文地址: 点击打开链接

Javascript Madness Intro

JavaScript Madness: Keyboard Events

Jan Wolter

1. Introduction

This document summarizes the results of some browser tests done while attempting to implement key stroke handling code in JavaScript. It documents inconsistancies in the way different browsers implement keyboard events.

The tests were originally done with the intention of learning just enough to write the code I needed to write. Coverage has expanded considerably since then, but the results here still are not comprehensive or authoritative and do not cover all aspects of keyboard event handling.

This data is based on tests of many, many browsers over many, many years, but is not comprehensive. I update it periodically as new browsers cross my desktop. The browser versions most recently tested are:

WindowsMacintoshLinux
Internet Explorer9.0.8112.164215.2-
Firefox5.0
(Gecko 5.0)
5.0.1
(Gecko 5.0.1)
4.0
(Gecko 2.0)
Safari4.0.4
(WebKit 531.21.10)
5.0.2
(WebKit 533.18.1)
-
Chrome3.0.195.33
(WebKit 532.0)
-4.0.249.43 Beta
(WebKit 532.5)
Opera10.539.1010.10
Konqueror--4.3.1
The script used to collect the test results reported here is available at http://unixpapa.com/js/testkey.html. I mostly report only what I can test myself, so this report is necessarily incomplete:
  • It primarily focuses on standard US keyboards. There are ahuge range of other keyboard layoutsin use in the world, which include not only different characters, but standard characters in different places. So, for example, many UK keyboards have a3 £key and a# ~key, neither of which exists on US keyboards. I don't know what keycodes keys like these send.

  • It does not cover the behavior of keypad keys on the Macintosh, because none of my Macs have keypads.

This document will usually refer to "Gecko" instead of "Firefox" and to "WebKit" instead of "Safari" or "Chrome". That's because browser behavior usually depends on the rendering engine, and different browsers that use the same rendering engine work the same. See theLayout Enginepage for more information, including mappings of layout engine versions to browser versions.

Previous versions of this document included coverage of the iCab 3 browser, but iCab has switched to using the WebKit rendering engine, and so presumably behaves exactly like Safari. Since it is unlikely that many web developers will want to go out of their way to support iCab 3, that material has been removed from this document and archived in aseparate report on iCab 3.

2. Event Triggering

In all recent browsers, pressing a key triggers a series of Javascript events which can be captured and handled. These events, however, were not defined by any standard until DOM3 which few browsers have yet implemented.

There is strong agreement across all browsers about which events should be sent and what order they should be sent in when a key is pressed:

BrowserEvents sent when normal key is typed
WebKit ≥ 525keydown
keypress
textInput

keyup
All Otherskeydown
keypress

keyup

Windows versions of Opera have a bit of buggy behavior: when you type the+,-,*, or/keys on thekeypad, then twokeypressevents are triggered instead of one. This has been observed on Opera 11 and Opera 8.5. I don't know how long this bug has been around.

Thekeydownevent occurs when the key is pressed, followed immediately by thekeypressevent, and possibly thetextInputevent. Then thekeyupevent is generated when the key is released.

To understand the difference betweenkeydownandkeypress, 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 the4key while holding down theShiftkey typically produces a "dollar sign" character. This is not necessarily the case on every keyboard in the world. In theory, thekeydownandkeyupevents represent keys being pressed or released, while thekeypressevent represents a character being typed. In practice, this is not always the way it is implemented.

textInputis a new event defined by the the DOM3 standard. So far, only the WebKit browsers supports it.textInputis a replacement and generalization ofkeypress(which is deprecated in DOM3). It is supposed to fire whenever text is input, whether by keyboard or not (it could be spoken text or cut/pasted text).

There are some oddities totextInputto be aware of. When setting up event handlers for it, you must useaddEventListener()orattachEvent()to set up the event handler. Older style methods won't work. Also you must capitalize it correctly when you pass the event name into the set up function. It must be "textInput" not "textinput".

2.1. Events Triggered by Special Keys
In addition to all the normal keys used to input ASCII characters, keyboards typically have many special purpose keys that do other things. These do not necessarily generate the same events as normal keys, and they show less consistency across browsers.

"Modifier keys" are one class of special keys. They include keys likeShift,ControlandAlt, that don't send characters, but modify the characters sent by other keys. For nearly all modern browsers, bothkeydownandkeyupevents are triggered by modifier keys, butkeypressevents are not. This is consistant with their being "key" events not "character" events.

However, Konqueror and some older browser versions do have different behaviors:

BrowserEvents sent when modifier keys are typed
Gecko ≥ 1.7
InternetExplorer
WebKit ≥ 525
Opera ≥ 10.10
keydown

keyup
Opera ≤ 9.50
Konqueror
keydown
keypress

keyup
WebKit < 525
Gecko 1.6
no events sent
Note that textInputis never fired for modifier keys, since they do not result in text entry.

Most browsers treat theCapsLockkey the same as any other modifier key, sendingkeydownwhen it is depressed andkeyupwhen it is released, but there are exceptions. Gecko browsers generate akeypressevent forCapsLockeven though they don't for other modifier keys. Macintosh versions of Safari 3 get really clever: each time you strike and release theCapsLockkey, only one event is triggered, and it iskeydownif you turning on caps-lock mode andkeyupif you are turning it off. Safari does not do this withNumLock.

There are many other special keys on a typical keyboard that do not normally send characters. These include the four arrow keys, navigation keys likeHomeandPage Up, special function keys likeInsertandDelete, and the function keysF1throughF12. Internet Explorer and WebKit 525 seem to classify all of these with the modifier keys, since they generate no text, so in those browsers there is nokeypressevent for them, onlykeyupandkeydown. Many other browsers, like Gecko, do generatekeypressevents for these keys, however.

Old versions of WebKit had a bug that caused two identicalkeyupevents to be triggered when arrow keys and other special keys were released. I know this existed in WebKit 312 and I know it was fixed in WebKit 525, but I don't know when it was fixed.

Standard Windows keyboards typically have twoStartkeys and aMenukey, while Apple keyboards have two Apple keys. I'm not going to attempt to describe the behavior of those keys in detail here. They are very inconsistent across browsers, don't exist on all keyboards, and they frequently have default actions that cannot be disabled. As such, Javascript programmers would be well advised to stay away from them.

IfNumLockis off, and you hit keypad number key while holdingShiftdown, then Windows systems trigger some extra events. Windows browsers pretend that theShiftkey was released before the key was typed, and then pressed again after it was released, and they triggerkeyup,keydownand (in some browsers)keypressevents to indicate this. Linux systems don't do this. I don't know if Macintoshes do.

2.2. Events Triggered on Auto-Repeat
If a key is held down long enough it typically auto-repeats, and some additional events will be triggered on each autorepeat. On Macintosh and Linux systems, modifier keys usually don't auto-repeat, but on Windows systems they do (which seems weird to me). In most browsers, an autorepeat is sensibly treated as a character event, but not a key event, so it triggers a keypressbut not a keydownor keyup. But, of course, there is some variation:
BrowserEvents triggered on each autoreapeat
normal keysspecial keys
InternetExplorer (Windows)keydown
keypress
keydown
WebKit ≥ 525keydown
keypress
textInput
keydown
Gecko (Windows)keydown
keypress
Gecko(SomeLinuxs)
Gecko(Macintosh)
WebKit < 525
Konqueror
Opera
keypressonly
Gecko(OherLinuxs)keyup
keydown
keypress
InternetExplorer(Macintosh)no events triggered
Gecko's behavior seems to be different on different versions of Linux. On some versions of Linux, mostly newer versions, it generates extra events, in a manner only previously seen on iCab 3. I don't know exactly what makes the difference.
2.3. Suppressing Default Event Handling
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.

BrowserWhich event handlers need to suppress defaults to prevent key from appearing in text box
InternetExplorer
Gecko
Konqueror 4.3
eitherkeydownorkeypress
WebKiteitherkeydown,keypressortextInput
Opera
Konqueror 3.5
keypress
Konqueror 3.2keydown

Suppressing defaults on thekeydownevent 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 thekeydownevent in these browsers.

BrowserSide effect suppressing defaults onkeydown
Gecko
WebKit < 525
Opera
No change
InternetExplorer keypressevent never occurs.
keyupevent works normally.
WebKit ≥ 525 keypressandtextInputevents never occur.
keyupevent works normally.
Konqueror keypressevent only occurs on auto repeats.
keyupevent works normally.
Note that WebKit also prevents textInputfrom firing if keypresssuppresses defaults. This makes sense, since suppressing the default action on either keydownor keypressprevents text entry, so since there is no text input, there should be no textInputevent. The DOM3 standards say that keyupshould still occur if the default action on keydownis suppressed, but textInputshould not.

In Konqueror 4.3.1, I noticed a brand new weirdness. If you don't suppress the default action onkeyupthen you get twokeyupevents. I also seemed to sometimes get duplicatekeydownandkeypressevents if defaults weren't suppressed on eitherkeydownorkeypress.

Most applications will either use onlykeypressor use onlykeyup/keydown, so this all works out pretty well in most browsers. If you are handlingkeypressand want to suppress default handling of the key, returnfalsefrom that handler. If you are handlingkeydown/keyupand want to suppress defaults, install akeypresshandler that does nothing except return false.

2.4. Event Triggering Summary
To give a clearer side by side comparison, suppose we press the Shiftkey, then press the Akey, 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 keydownhandler that returns false.
Internet Explorer
(Windows)
Gecko
(Windows)
Gecko
(Linux/Mac)
Opera≥10.10
WebKit≥525WebKit<525Opera≤9.50KonquerorInternet Explorer
(Mac)
Shiftpressed keydown
keydown
keydown
keydown
keydown
keypress
keydown
keypress
keydown
Apressedkeydown
keypress
keydown
keypress
keydown
keypress
keydown
keypress
textInput
keydown
keypress
keydown
keypress
keydown
keypress
keydown
keypress
Aautorepeats
keydown
keypress

keydown
keypress


keypress

keydown
keypress
textInput


keypress


keypress


keypress
Areleasedkeyupkeyupkeyupkeyupkeyupkeyupkeyupkeyup
Shiftreleasedkeyupkeyupkeyupkeyupkeyupkeyupkeyup

I used to exclaim here about no two browsers being alike here, but progress is being made. The newer versions of WebKit are extremely close to IE, differing only in being the first to support the newtextInputevent, and Opera now behaves almost identically with Linux/Mac versions of Gecko.

3. Identifying Keys

When you catch a keyboard event, you may wish to know which key was pressed. If so, you may be asking too much. This is a very big mess of browser incompatibilities and bugs.
3.1. Classic Values Returned on Key Events
The keydownand keyupevents 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 will call these "Mozilla keycodes", "IE keycodes", "Opera keycodes" and "psuedo-ASCII codes" and we'll explain them in more detail below.

Not only do the browsers differ in what values they return, they differ in where they return them. Three different properties of the event object may be used to return them. They areevent.keyCode,event.whichandevent.charCode.

keydown and keyup events
event.keyCodeevent.whichevent.charCode
IE<9.0(Windows)IE keycodeundefinedundefined
Internet Explorer(Mac)IE keycodeundefinedextended ASCII code
IE≥9.0
WebKit ≥ 525
IE keycodeIE keycodezero
WebKit < 525IE keycodeIE keycodeASCII code if ASCII character,
zero otherwise
GeckoMozilla keycodeMozilla keycodezero
Opera ≥ 9.50 (all platforms)
Opera 7 (Windows)
Mozilla keycode except keypad and branded keys give Opera keycodesMozilla keycode except keypad and branded keys give Opera keycodesundefined
Opera 8.0 to 9.27 (Windows)Opera keycodeOpera keycodeundefined
Opera < 9.50 (Linux & Macintosh)Pseudo-ASCII codePseudo-ASCII codeundefined
Konqueror 4.3Pseudo-ASCII codePseudo-ASCII codezero
Konqueror 3.5Pseudo-ASCII codePseudo-ASCII code if key has an ASCII code,
zero otherwise
zero
Konqueror 3.2Pseudo-ASCII codePseudo-ASCII codeundefined
In version 9.50, Opera abandoned Opera keycodes and Pseudo-ASCII keycodes in favor of Mozilla keycodes for most keys (thus reverting to the behavior of Windows Opera 7). WebKit has modified their Konqueror-derived code to use IE keycodes, and I expect Konqueror will follow. Thus there seems to be a convergences on the IE and Mozilla keycodes, which are pretty similar. This is kind of encouraging.

Onkeydownandkeyup, the event objects also have flags that indicate which modifier keys were being pressed when the key was typed. These are:

   event.shiftKey
   event.ctrlKey
   event.altKey
   event.metaKey
These all have trueor falsevalues. According to the DOM 3 standard, on the Macintosh, the Optionkey should activate event.altKeyand the Commandkey should activate event.metaKey. These attributes seem to work correctly on all modern browsers tested, except event.metaKeyis undefined in all versions IE. There is some freakishness in obsolete browsers that can probably be ignored these days. In Macintosh versions of IE, the Commandkey sets event.ctrlKeyand the Controlkey does nothing. In Netscape 4, none of these attributes existed and the event.modifiersattribute needed to be used instead.

One would think that if a key is typed whenCaps Lockis on, thenevent.shiftKeywould betrue, but this is not the case in any browser tested. There is also a lot of inconsistency in the values these flags take on thekeydownandkeyupevents actually associated with pressing and releasing the modifier keys, but I can't imagine anyone would care enough to justify documenting the details.

3.2. Classic Values Returned on Character Events
For keypressevents, it is pretty clear that the ASCII code of the typed character should be returned, and pretty much all browsers do that.

But what if there is no ASCII code associated with the key? Arrow keys and keys likePage DownandF1don't have ASCII codes. We call these "special" keys in contrast to the "normal" keys that have ASCII codes. Note thatEsc,Backspace,Enter, andTabare "normal" because they have ASCII codes.

Whenkeypressevents are generated for special keys, the browser needs to return some non-ASCII value to indicate which key ways pressed. We'll see that various different browsers do this in different ways.

Some browsers avoid this problem by not generatingkeypressevents for special keys. A good case can be made that this is the right thing to do, since these keystrokes are arguably not character events. But such arguments are weakened by the arbitrariness of the division between normal and special keys. Why should the keyboardBackspacekey have akeypressevent, but not the keypadDeletekey? IsTabreally fundamentally different thanright arrow?

keypress events
event.keyCodeevent.whichevent.charCode
IE <9.0 (Windows)normal:ASCII codeundefinedundefined
special:no keypress events for special keys
IE (Mac)normal:ASCII codeundefinedASCII code
special:no keypress events for special keys
Geckonormal:zeroASCII codeASCII code
special:Mozilla keycodezerozero
IE ≥ 9.0
WebKit ≥ 525
normal:ASCII codeASCII codeASCII code
special:no keypress events for special keys
WebKit < 525normal:ASCII codeASCII codeASCII code
special:extended ASCII codeextended ASCII codeextended ASCII code
Opera ≥ 10.50 (all platforms)normal:ASCII codeASCII codeundefined
special:Mozilla keycode, except keypad and branded keys give Opera keycodeszeroundefined
Opera ≥ 9.50 (all platforms)
Opera 7 (Windows)
normal:ASCII codeASCII codeundefined
special:Mozilla keycode, except keypad and branded keys give Opera keycodeszero for arrows, function keys, PageUp, PageDown
same asevent.keyCodeotherwise
undefined
Opera 8.0 to 9.27 (Windows)normal:ASCII codeASCII codeundefined
special:Opera keycodezero for arrows, function keys, PageUp and PageDown,
same asevent.keyCodeotherwise
undefined
Opera < 9.50 (Linux & Macintosh)normal:ASCII codeASCII codeundefined
special:Opera keycodezero for arrows, function keys, PageUp and PageDown,
same asevent.keyCodeotherwise
undefined
Konqueror 4.3normal:ASCII codeASCII codeASCII code
special:Pseudo-ASCII codePseudo-ASCII codezero
Konqueror 3.5normal:ASCII codeASCII codeASCII code
special:Pseudo-ASCII codezerozero
Konqueror 3.2normal:ASCII codeASCII codeundefined
special:no keypress events for special keys
The traditional method to distinguish special keys from normal keys on keypressevents is to first check event.which. If it is undefined or non-zero, then the event is from a normal key, and the ASCII code for that key is in event.keyCode. If it is defined as zero, the the event is from a special key, and the keycode is in event.keyCode. This works for almost every browser, but there are two exceptions:
  • The newest version of Konqueror that I have tested, version 4.3.1, returns non-zeroevent.whichvalues for all special keys. The only way to distinguish an up arrow from an ampersand is to checkevent.charCode.
  • Versions of Opera before 10.50 messes up by returning non-zeroevent.whichvalues for four special keys (Insert,Delete,HomeandEnd).
So, I guess with this new botched version of Konqueror, we have to make our tests more complex. If neither event.whichnor event.charCodeis defined as zero, then it is a normal key event.

The DOM 3 standard makes a half-hearted attempt to suggest standards for these "legacy" attributes. Konqueror's annoying non-zeroevent.whichvalues for special keys actually kind of comply with what it suggests. Except by that standard,keypressshouldn't be firing at all for special keys.

If you are actually interested in special key events, then probably you should be hooking your code intokeydownandkeyup, which work more consistently across browsers. So the main practical importance of this is thatkeypresshandlers should not treatevent.keyCodeas an ASCII code if eitherevent.whichorevent.charCodeis defined as zero.

The flagsevent.shiftKey,event.ctrlKey,event.altKeyandevent.metaKeyare typically defined onkeypressevents, just as they are onkeydownandkeyupevents. WebKit seems to defineevent.keyIdentifieronkeypressas well, but I wouldn't count on future browsers doing that.

OntextInputevents,event.datacontains the text that was input. On key inputs, this is typically a one character string. Since there are notextInputevents on special keys, we don't have to worry about such cases. In WebKit, at least, none of the old traditional values mentioned above are defined ontextInputevents.

3.3. Key Code Values
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.

The table below lists values for keys commonly found on US keyboards. If a single value is given, then that value is sent whether theShiftkey is held down or not. If two valuesx/yare given, the the first is sent when the key is unshifted, and the second is sent when the key is shifted. (Ideally there should be slashed values only in the ASCII column of this table, since the other codes are all used only onkeyupandkeydownevents, which are key events not character events.)

Keys highlighted in green are consistent across all browsers tested. Keys highlighted in yellow are consistent for recent versions of IE, Gecko, WebKit and Opera. Keys highlighted in red aren't.

KeyASCIIMozilla keycodesIE keycodesOpera keycodespseudo ASCII codesexceptions
Alphabetic keys
AtoZ
97/65 to 122/90ASCII code of uppercase version of the letter
65 to 90
Space3232323232
Enter1313131313
Tab99999
Esc2727272727
Backspace88888
Modifier Keys
KeyASCIIMozilla keycodesIE keycodesOpera keycodespseudo ASCII codesexceptions
Shift-16161616 Linux Opera < 9.0: 0
Control-17171717 Linux Opera < 9.0: 0
Mac Opera: 0
Alt-18181818 Linux Opera < 9.0: 0
CapsLock-20202020 Linux Opera: 0
NumLock-144144144144 Linux Opera < 9.50: 0
Win Opera < 9.00: 0
Keyboard Number Keys
KeyASCIIMozilla keycodesIE keycodesOpera keycodespseudo ASCII codesexceptions
1 !49/3349494949/33 Mac Gecko < 1.8: 49/0
2 @50/6450505050/64 Mac Gecko < 1.9: 50/0
3 #51/3551515151/35 Mac Gecko < 1.9: 51/0
4 $52/3652525252/36 Mac Gecko < 1.9: 52/0
5 %53/3753535353/37 Mac Gecko < 1.9: 53/0
6 ^54/9454545454/94 Mac Gecko < 1.9: 54/0
7 &55/3855555555/38 Mac Gecko < 1.9: 55/0
8 *56/4256565656/42 Mac Gecko < 1.9: 56/0
9 (57/4057575757/40 Mac Gecko < 1.9: 57/0
0 )48/4148484848/41 Mac Gecko < 1.9: 48/0
Symbol Keys
KeyASCIIMozilla keycodesIE keycodesOpera keycodespseudo ASCII codesexceptions
; :59/58591865959/58 Mac Gecko: 59/0
= +61/43611876161/43 Mac Gecko ≥ 1.9: 61/107
Mac Gecko < 1.9: 61/0
, <44/601881884444/60 Mac Gecko: 188/0
- _45/951091894545/95 Mac Gecko ≥ 1.9: 109/0
Mac Gecko < 1.9: 0
. >46/621901904646/62 Mac Gecko: 190/0
/ ?47/631911914747/63 Mac Gecko: 191/0
` ~96/1261921929696/126 Mac Gecko: 192/0
[ {91/1232192199191/123
\ |92/1242202209292/124 Mac Gecko: 220/0
] }93/1252212219393/125
' "39/342222223939/34
Arrow Keys
KeyASCIIMozilla keycodesIE keycodesOpera keycodespseudo ASCII codesexceptions
left-arrow-37373737
up-arrow-38383838
right-arrow-39393939
down-arrow-40404040
Special Keys
KeyASCIIMozilla keycodesIE keycodesOpera keycodespseudo ASCII codesexceptions
Insert-45454545 Konqueror: 0
Opera < 9.0: 0
Delete-46464646 Konqueror: 127
Opera < 9.0: 0
Home-36363636 Opera < 9.0: 0
End-35353535 Opera < 9.0: 0
Page Up-33333333
Page Down-34343434
Function Keys
F1toF12
-112 to 123112 to 123112 to 123112 to 123
Keypad Keys
If Num Lock is on, unshifted/shifted values are returned as shown below. If Num Lock is off, Linux browsers reverse the shifted/unshifted values, while Windows browsers always return the shifted value. None of my Macintoshs have a keypad, so I don't know what they do.
KeyASCIIMozilla keycodesIE keycodesOpera keycodespseudo ASCII codesexceptions
. Del46/-110/46110/4678/4678/46 Opera < 9.0: 78/0
Linux Opera 11.5: 190/46
0 Ins48/-96/4596/4548/4548/45 Opera < 9.0: 48/0
1 End49/-97/3597/3549/3549/35 Opera < 9.0: 49/0
2 down-arrow50/-98/4098/4050/4050/40
3 Pg Dn51/-99/3499/3451/3451/34
4 left-arrow52/-100/37100/3752/3752/37
553/-101/12101/1253/1253/12 Linux Opera: 53/0
6 right-arrow54/-102/39102/3954/3954/39
7 Home55/-103/36103/3655/3655/36 Opera < 9.0: 55/0
8 up-arrow56/-104/38104/3856/3856/38
9 Pg Up57/-105/33105/3357/3357/33
+431071074343 Linux Opera 11.5: 61
-451091094545 Linux Opera 11.5: 109
*421061064242 Linux Opera 11.5: 56
/471111114747 Linux Opera 11.5: 191
KeypadEnter 1313131313
Branded Keys
KeyASCIIMozilla keycodesIE keycodesOpera keycodespseudo ASCII codesexceptions
Left AppleCommand -224?17? WebKit ≥ 525: 91
Right AppleCommand -224?17? WebKit ≥ 525: 93
Left WindowsStart -91912190 Linux Gecko: 0
Right WindowsStart -92922200 Linux Gecko: 0
WindowsMenu -939300
Note that all four encodings agree on most of the common keys, the ones highlighted 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).

For symbols, things are a fair mess. IE and Mozilla don't entirely agree on what the codes should be. Three keys,;:,=+and-_, have different values in IE and Mozilla keycodes. Furthermore, there are long standing bugs in Macintosh versions of Gecko that have caused zero keyCodes to be returned for many symbols.

The Opera keycodes have been abandoned by Opera, but they had a certain simple charm. They were 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.

The pseudo ASCII codes weren't really keycodes at all. They were just the ASCII code for the characterexceptthat for lower case letters the upper case ASCII code is sent. So those browsers 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 keycodes, 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 WebKit's approach, where they keep the keycodes (making them entirely compatible with IE keycodes) butalsoreturn the character code on all key events.

Using pseudo-ASCII codes causes another problem: you can't always recognize the arrow keys onkeydownandkeyupevents. 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 "(". On U.S. keyboards all those five characters are sent by shifted keys, so you'll never see them as keycodes under any of the three keycode schemes. (Some foreign keyboards do create these characters from unshifted keys, but I don't know what keycodes are sent by those keys.) But when pseudo-ASCII keycodes are used these same values are also sent when you type those keys, so you can't tell those symbols from arrow keys. Similar problems occur with some of the other special keys likeHomewhich sends the same values as "$".

For browsers that generatekeypressevents for special keys, it is also generally true thatevent.keyCodewill have the same value for "left-arrow" and "%", however we can usually tell which it is becauseevent.whichis zero for special keys (there are problems with this in Opera and Konqueror, see above). Versions of WebKit before 525 took a different approach. They invented unique values to return instead of ASCII codes for special keys, and returned the same value inevent.keyCode,event.which, andevent.charCode. The table below gives the extended ASCII codes returned by old WebKit versions, and also the ones returned inevent.charCodeonkeydownandkeyupevents in Macintosh versions of IE.

keyExtended ASCII codes for Special Keys
up arrowdown arrowleft arrowright arrowfunction keys
F1 to F12
HomeEndPage UpPage Down
WebKit < 5256323263233632346323563236 to 6324763273632756327663277
Macintosh IE3031282916 for all keysno events triggered

To complete the thoroughness of the mess, keycode generation in current Macintosh versions of Gecko remains buggy. For many keys, no keycodes are returned onkeydownandkeyupevents. Instead thekeyCodevalue is just zero. Some of these problems were fixed in Gecko 1.9, but not all, and the keyboard plus key started returning the value that is supposed to be returned by the number pad plus key.

charactersKeycodes on Geckokeyupandkeydownevents
Linux and Windows
Gecko (correct)
Macintosh Gecko 1.8 and older (buggy)Macintosh Gecko 1.9 and later (buggy)
! @ # $ % ^ & * ( )Same as number keys these symbols appear onzeroSame as number keys these symbols appear on
-109zero109
_ ~ | < > ? :Same as unshifted symbol keys these symbols appear onzerozero
+61zero107
Any key typed with ALT key held downSame code as without ALT keyzeroSame code as without ALT key
Macintosh Gecko does give correct charCode values on keypressevents, but to a keydownor keyuphandler, all the keys that return zero above are indistinguishable. This bug was reported to Mozilla (bug 44259) in June 2000, and it took eight years to get the partial fixes out. Who knows when the rest ( 48434) will be fixed.
3.4. New Standard Key and Character Events
The DOM3 standard abandons all hope of creating order among event.keyCode, event.whichand event.charCode, and instead defines new values for keydownand keyupevents. It deprecates the keypressevent and replaces it with the textInputevent. Unfortunately, the standard changed recently, so that a few browsers implemented an older version of the standard which is quite different from the current version.

Earlier versions of the specification defined attributes namedevent.keyIdentifierandevent.keyLocation. ThekeyIdentifierwas a string that in most cases looked like "U+0041" where the "0041" part is the unicode value of the character sent by the key when it is typed without modifiers, in this case the letter "A". For keys that didn't send unicode characters, or where the unicode value is not standardized, it was a string like "Enter", "Shift", "Left" or "F9". ThekeyLocationattribute gave values to distinguish among multiple keys that had the same identifier, like the left and right shift keys, or the keypad number keys. It was 0 for standard keys, 1 or 2 for left or right versions of a keys likeShiftwhich appear twice on the keyboard, and 3 for keys on the numeric keypad.

WebKit implemented support forkeyIdentifierand got it mostly right. Older versions conformed to an older version of the standard and returned two extra zeros (eg, "U+000041") but this was corrected in version 525. Windows versions of Safari and Linux versions of Chrome return badkeyIdentifiervalues for all of the non-number symbol keys (WebKit Bug19906reported in July 2008). ThekeyLocationattribute is always 0 or 3, so it does not distinguish between left and right modifier keys.

Konqueror returnskeyIdentifiervalues 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". AllkeyLocationvalues are zero, except for modifiers key, which are always one, regardless of whether the left or right one was pressed.

We cannot, however expect any more browsers to implement that standard, since it has now changed. TheDOM 3 standardno longer mentionsevent.keyIdentifierorevent.keyLocation. Instead we haveevent.key,event.char,event.location.. So far as I know, no browser has yet implemented this new version of the DOM 3 standard.

In this standardevent.charis defined only when you type a printable character, or another character with a defined code (like tab or backspace). It's basically likeevent.charCodeexcept that it is the character, not the character code and can be any unicode character not just an ASCII code.Event.keyis the same asevent.charfor printable keys. For other keys, even ones like tab or backspace that have character encodings, it is a string like'Tab','Left'or'F9'. These values are supposed to be the same onkeypressevents as they are onkeyupandkeydownevents, thoughkeypresswould not be fired for those cases whereevent.charis null.

Note that neither of these pretends to be a keycode identifying a particular physical key on the keyboard. If you press the/?key on a US keyboard while shift is off, but press the shift key before releasing the/?key, then then onkeydownyou'll getevent.key=='/'and onkeyupyou'll getevent.key=='?'. The only way your Javascript program will know that those two events go together is if it happens to know that those two characters are on the same key. There is anevent.localevalue that is supposed to give you some clue on what type of keyboard is being used, but figuring out what keys go with what on a particular keyboard is up to you.

Clearly this abandonment of the idea of keycodes is going to cause problems, but is still probably justified. In many (most?) operating systems, I don't think the browser can actually tell which key was pressed. In the browser source code I've seen, the keycodes are generated from the the character codes, not vice versa, by simply assuming that the character came from a US keyboard. So the keycode values never really worked for non-US keyboards.

So while the keycode concept was a handly one, it isn't really practically extensible in the real world. If you want a keycode in the DOM 3 universe, you'll have to go on using the legacyevent.keyCodevalue, which, standards or no standards, isn't going away. The DOM 3 standard seems to recognize this, and reluctantly provides anappendixwith some standards forevent.keyCodeand the like. It casts a rather weak vote for what I called "IE keycodes" above.

Conclusions

It's truely impressive what a hash has been made of a simple thing like recognizing a key pressed on the keyboard. You'd think computer technology would have advanced far enough by now to have this all worked out better.

Thekeypressevents are generally the easiest to work with. They are likely to cause substantially fewer problems with non-US keyboard layouts and it's not too hard to identify which key was pressed. You can get the character typed by doing:

  if (event.which == null)
     char= String.fromCharCode(event.keyCode);    // old IE
  else if (event.which != 0 && event.charCode != 0)
     char= String.fromCharCode(event.which);	  // All others
  else
     // special key
What to do with keypressevents on special keys is a problem. I recommend pretending they never happened. If you really want to process special key events, you should probably be working with keydownand keyupinstead.

Forkeydownandkeyupevents, you can identify most common keys (letters, numbers, and a few others) by just looking at theevent.keyCodeand 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. Onkeydownandkeyupevents, the keycodes arenotcharacter 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. I don't have information on keycodes sent by international keyboards.

Because of bugs, many keys cannot be distinguished onkeydownandkeyupin Macintosh Gecko.

Hope for sanity exists, with the new key event handling specifications in DOM3, but so far only WebKit implements them.


Last Update: Fri Aug 12 14:07:10 EDT 2011
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值