JavaScript Madness不同的浏览器的keycode比较
来源:广州中睿信息技术有限公司官网
发布时间:2012/10/21 23:25:16 编辑:admin 阅读 283
JavascriptMadnessIntroJavaScriptMadness:KeyboardEventsJanWolter1.IntroductionThisdocumentsummarizes
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:

  Windows Macintosh Linux
Internet Explorer 9.0.8112.16421 5.2 -
Firefox 5.0
(Gecko 5.0)
5.0.1
(Gecko 5.0.1)
4.0
(Gecko 2.0)
Safari 4.0.4
(WebKit 531.21.10)
5.0.2
(WebKit 533.18.1)
-
Chrome 3.0.195.33
(WebKit 532.0)
- 4.0.249.43 Beta
(WebKit 532.5)
Opera 10.53 9.10 10.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 a huge range of other keyboard layouts in use in the world, which include not only different characters, but standard characters in different places. So, for example, many UK keyboards have a 3 £ 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 the Layout 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 a separate 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:

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

keyup
All Others keydown
keypress

keyup

Windows versions of Opera have a bit of buggy behavior: when you type the +, -, *, or / keys on the keypad, then two keypressevents 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.

The keydown event occurs when the key is pressed, followed immediately by the keypress event, and possibly the textInput event. Then the keyupevent 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 keypressevent represents a character being typed. In practice, this is not always the way it is implemented.

textInput is a new event defined by the the DOM3 standard. So far, only the WebKit browsers supports it. textInput is a replacement and generalization of keypress(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 to textInput to be aware of. When setting up event handlers for it, you must use addEventListener() or attachEvent() 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 like Shift, Control and Alt, that don't send characters, but modify the characters sent by other keys. For nearly all modern browsers, both keydown and keyup events are triggered by modifier keys, but keypressevents are not. This is consistant with their being "key" events not "character" events.

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

Browser Events sent when modifier keys are typed
Gecko ≥ 1.7
Internet Explorer
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 the Caps Lock key the same as any other modifier key, sending keydown when it is depressed and keyup when it is released, but there are exceptions. Gecko browsers generate a keypress event for Caps Lock even though they don't for other modifier keys. Macintosh versions of Safari 3 get really clever: each time you strike and release the Caps Lock key, only one event is triggered, and it is keydown if you turning on caps-lock mode and keyup if you are turning it off. Safari does not do this with Num Lock.

There are many other special keys on a typical keyboard that do not normally send characters. These include the four arrow keys, navigation keys like Home and Page Up, special function keys like Insert and Delete, and the function keys F1 through F12. 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 no keypress event for them, only keyup and keydown. Many other browsers, like Gecko, do generate keypressevents for these keys, however.

Old versions of WebKit had a bug that caused two identical keyupevents 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 two Start keys and a Menukey, 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.

If NumLock is off, and you hit keypad number key while holding Shift down, then Windows systems trigger some extra events. Windows browsers pretend that the Shift key was released before the key was typed, and then pressed again after it was released, and they trigger keyup, keydown and (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 keypress but not a keydown or keyup. But, of course, there is some variation:

Browser Events triggered on each autoreapeat
normal keys special keys
Internet Explorer (Windows) keydown
keypress
keydown
WebKit ≥ 525 keydown
keypress
textInput
keydown
Gecko (Windows) keydown
keypress
Gecko (Some Linuxs)
Gecko (Macintosh)
WebKit < 525
Konqueror
Opera
keypress only
Gecko (Oher Linuxs) keyup
keydown
keypress
Internet Explorer (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.

Browser Which event handlers need to suppress defaults to prevent key from appearing in text box
Internet Explorer
Gecko
Konqueror 4.3
either keydown or keypress
WebKit either keydown, keypress or textInput
Opera
Konqueror 3.5
keypress
Konqueror 3.2 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 keydownevent in these browsers.

Browser Side effect suppressing defaults on keydown
Gecko
WebKit < 525
Opera
No change
Internet Explorer keypress event never occurs.
keyup event works normally.
WebKit ≥ 525 keypress and textInput events never occur.
keyup event works normally.
Konqueror keypress event only occurs on auto repeats.
keyup event works normally.

Note that WebKit also prevents textInput from firing if keypress suppresses defaults. This makes sense, since suppressing the default action on either keydown or keypress prevents text entry, so since there is no text input, there should be no textInput event. The DOM3 standards say that keyup should still occur if the default action on keydown is suppressed, but textInputshould not.

In Konqueror 4.3.1, I noticed a brand new weirdness. If you don't suppress the default action on keyup then you get two keyup events. I also seemed to sometimes get duplicate keydown and keypress events if defaults weren't suppressed on either keydown or keypress.

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 keypresshandler that does nothing except return false.

2.4. Event Triggering Summary

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.

  Internet Explorer
(Windows)
Gecko
(Windows)
Gecko
(Linux/Mac)
Opera≥10.10
WebKit≥525 WebKit<525 Opera≤9.50 Konqueror Internet Explorer
(Mac)
Shift pressed keydown keydown keydown keydown   keydown
keypress
keydown
keypress
keydown
A pressed keydown
keypress
keydown
keypress
keydown
keypress
keydown
keypress
textInput
keydown
keypress
keydown
keypress
keydown
keypress
keydown
keypress
A autorepeats
keydown
keypress

keydown
keypress


keypress

keydown
keypress
textInput


keypress


keypress


keypress
 
A released keyup keyup keyup keyup keyup keyup keyup keyup
Shift released keyup keyup keyup keyup   keyup keyup keyup

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 new textInputevent, 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 keydown and 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 are event.keyCode, event.which and event.charCode.

keydown and keyup events
  event.keyCode event.which event.charCode
IE <9.0 (Windows) IE keycode undefined undefined
Internet Explorer (Mac) IE keycode undefined extended ASCII code
IE ≥ 9.0
WebKit ≥ 525
IE keycode IE keycode zero
WebKit < 525 IE keycode IE keycode ASCII code if ASCII character,
zero otherwise
Gecko Mozilla keycode Mozilla keycode zero
Opera ≥ 9.50 (all platforms)
Opera 7 (Windows)
Mozilla keycode except keypad and branded keys give Opera keycodes Mozilla keycode except keypad and branded keys give Opera keycodes undefined
Opera 8.0 to 9.27 (Windows) Opera keycode Opera keycode undefined
Opera < 9.50 (Linux & Macintosh) Pseudo-ASCII code Pseudo-ASCII code undefined
Konqueror 4.3 Pseudo-ASCII code Pseudo-ASCII code zero
Konqueror 3.5 Pseudo-ASCII code Pseudo-ASCII code if key has an ASCII code,
zero otherwise
zero
Konqueror 3.2 Pseudo-ASCII code Pseudo-ASCII code undefined

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.

On keydown and keyup, 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 true or false values. According to the DOM 3 standard, on the Macintosh, the Option key should activate event.altKey and the Command key should activate event.metaKey. These attributes seem to work correctly on all modern browsers tested, except event.metaKey is 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 Command key sets event.ctrlKey and the Control key 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 when Caps Lock is on, then event.shiftKey would be true, but this is not the case in any browser tested. There is also a lot of inconsistency in the values these flags take on the keydown and keyupevents 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 like Page Down and F1 don't have ASCII codes. We call these "special" keys in contrast to the "normal" keys that have ASCII codes. Note that Esc, Backspace, Enter, and Tabare "normal" because they have ASCII codes.

When keypressevents 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 generating keypress events 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 keyboard Backspace key have a keypress event, but not the keypad Delete key? Is Tab really fundamentally different than right arrow?

keypress events
  event.keyCode event.which event.charCode
IE < 9.0 (Windows) normal: ASCII code undefined undefined
special: no keypress events for special keys
IE (Mac) normal: ASCII code undefined ASCII code
special: no keypress events for special keys
Gecko normal: zero ASCII code ASCII code
special: Mozilla keycode zero zero
IE ≥ 9.0
WebKit ≥ 525
normal: ASCII code ASCII code ASCII code
special: no keypress events for special keys
WebKit < 525 normal: ASCII code ASCII code ASCII code
special: extended ASCII code extended ASCII code extended ASCII code
Opera ≥ 10.50 (all platforms) normal: ASCII code ASCII code undefined
special: Mozilla keycode, except keypad and branded keys give Opera keycodes zero undefined
Opera ≥ 9.50 (all platforms)
Opera 7 (Windows)
normal: ASCII code ASCII code undefined
special: Mozilla keycode, except keypad and branded keys give Opera keycodes zero for arrows, function keys, PageUp, PageDown
same as event.keyCode otherwise
undefined
Opera 8.0 to 9.27 (Windows) normal: ASCII code ASCII code undefined
special: Opera keycode zero for arrows, function keys, PageUp and PageDown,
same as event.keyCode otherwise
undefined
Opera < 9.50 (Linux & Macintosh) normal: ASCII code ASCII code undefined
special: Opera keycode zero for arrows, function keys, PageUp and PageDown,
same as event.keyCode otherwise
undefined
Konqueror 4.3 normal: ASCII code ASCII code ASCII code
special: Pseudo-ASCII code Pseudo-ASCII code zero
Konqueror 3.5 normal: ASCII code ASCII code ASCII code
special: Pseudo-ASCII code zero zero
Konqueror 3.2 normal: ASCII code ASCII code undefined
special: no keypress events for special keys

The traditional method to distinguish special keys from normal keys on keypress events 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-zero event.which values for all special keys. The only way to distinguish an up arrow from an ampersand is to check event.charCode.
  • Versions of Opera before 10.50 messes up by returning non-zero event.which values for four special keys (Insert, Delete, Home and End).

So, I guess with this new botched version of Konqueror, we have to make our tests more complex. If neither event.which nor 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-zero event.which values for s

联系我们CONTACT 扫一扫
愿景:成为最专业的软件研发服务领航者
中睿信息技术有限公司 广州•深圳 Tel:020-38931912 务实 Pragmatic
广州:广州市天河区翰景路1号金星大厦18层中睿信息 Fax:020-38931912 专业 Professional
深圳:深圳市福田区车公庙有色金属大厦509~510 Tel:0755-25855012 诚信 Integrity
所有权声明:PMI, PMP, Project Management Professional, PMI-ACP, PMI-PBA和PMBOK是项目管理协会(Project Management Institute, Inc.)的注册标志。
版权所有:广州中睿信息技术有限公司 粤ICP备13082838号-2