Web 键盘输入法应用开发指南 (2) —— 键盘事件
介绍
在上一篇文章中,我简单介绍了与 Web 键盘和输入法应用相关的关键技术点。从这篇文章开始我们针对各个主题深入,看有哪些在开发中值得注意的地方。这里先从浏览器支持的与输入相关事件(按键、输入等)开始。
keydown/keyup/keypress 事件
keydown[1]和 keyup 事件通常会成对出现(也有例外),它们基本上是用户在按下(释放)某个键时,浏览器最先触发的事件。这里还需注意两点:
无论用户是否在使用输入法,keydown 事件总是会触发,即便输入控件中没有任何字符。比如正在用输入法拼字,并且还没有提交。
在一些特殊的平台或者浏览器(比如 Android Chrome 或者 Mac Firefox 等等),keydown 事件不一定时最先触发的,一些输入法相关的事件(如 compostionstart)会更早触发。这对于有些依赖于事件顺序的业务逻辑会造成影响,而且由于平台相关,与之相关的 bug 也不易发现。
keypress 事件[7]已经被标准废弃,不建议继续使用。它表示的是实际产生字符值的按键事件,比如 ALT、CTRL 之类功能键就不会触发该事件。不过即使产生了字符输出,该事件也不一定触发,这跟浏览器的实现有关,关键逻辑建议不要依赖这个事件。
keyCode 属性
在一个按键事件中,key、code 和 keyCode 这三个属性可以用来识别哪个键被按下了。key 和 code 属性是字符串,而 keyCode 是一个整数。在一些比较老的应用上,我们会看到 keyCode 的广泛应用。比如 keyCode=65 表示字母 a 被按下,keyCode=18 是 Alt 键,而 keyCode=32 则表示空格键。不过官方文档[2]已经不推荐使用这个属性了,因为它已经被 Web 标准逐渐废弃,各个浏览器厂商也会逐渐不再支持。keyCode 值是因平台和浏览器而异的,在使用时要特别注意,具体的取值可以参考 MDN 的官方文档。
对于 keyCode 还有一些特殊情况,比如在使用输入法时,按下某个键会对应一个键盘上没有的字符(比如韩文的),这次按键其实也有 keyCode,其取值通常较大(例如韩文字符基本在 4000 以上,准确的说是keypress
事件的 keyCode),这个信息可以作为判断当前输入语言的一个参考。对于中文和日文来说又不太一样,在拼写中文字符时,每次按键都会返回一个229
的 keyCode 值,通过这个值可以过滤掉使用输入法时的 keydown 事件。
总的来说,使用 keyCode 要时刻考虑浏览器、语言及平台的兼容性,并且 keyCode 的值不易记忆,给开发和调试带来难度,因此对于新的应用不推荐使用。那么怎么拿到准确的键值呢?答案是使用 key 和 code 属性。
key 属性
简单来说,key 属性[3]告诉你的按键产生了什么值,这个属性名稍有些迷惑性(叫“键”,实际是“值”)。比如在一个英文键盘上,你按“A”键,那对应的 key event 中 key 属性的值就是“a”。
需要注意的是 key 的值是最终的输出值,也就是它也考虑了 SHIFT 等功能键的效果、系统的 locale 以及键盘的 layout 等信息。比如,如果按的是 SHIFT+A 组合,那么 key 的属性值是大写的“A";在韩文输入法下,按右边的 CTRL 键,其实是韩语输入的 Hanja key(用于输入汉字),对应 key 的属性值为“HanjaMode”(而不是 CTRL 本身);在日语的 106/109 键盘上,按特殊的 Kana/罗马字转换键,会产生“KanaMode”的 key 属性值。
注意,产生的这个 key 值如果是一个可打印的字符(比如字母、数字、符号等),那么会进一步出发input
等事件,将字符输入某个文本控件内。如果只是功能键(比如 Hanja Key)则不会有input
事件产生。因此,有时我们在拦截用户输入时input
事件不一定来得及,并不是每次按键都产生实际输入。
在一些输入法的场景中,有用的 key 属性可能出现在 keypress 事件中。比如使用中文输入法输入一些全角字符时(中文的¥
,【
,》
等等),在 keydown 和 keyup 事件中,key 属性可能只是“Process”,表示这个按键被输入法处理了,而在 keypress 事件中的 key 属性却可以得到准确的中文全角字符。针对这种情况,开发者还要注意平台的差异性,根据我的经验,在 Ubuntu 的 Firefox 浏览器中,keypress 事件就无法产生,可以认为是一个 bug。不过在上诉场景中,通常还有input
事件产生,可以用来输入的处理,但不一定是好的时机。
如果所在的平台和浏览器无法识别某个键盘的按键,那么按键事件依然会产生,只不过 key 属性的值为“Unidentified”。在处理按键事件时要时刻牢记三个影响因素:键盘布局、操作系统和浏览器。还有一种特殊的 key 称为 Dead Key[5],在输入某些欧洲语言的 accent 字符时,需要先按一次口音符号`,再按字母键(比如在法语中的à=`+a),这里的口音符号就会产生“Dead”的 key 值,而 a 键的 key 属性为最终的字符à
。
code 属性
有时候,我们可能不关心某个键的输出是什么,而只是关心当前键盘上哪个位置的键被按下了,这其实是像获取按键的Scan Code
。Scan Code 在不同的平台上有不同的表示,同时还与外接的键盘有关(比如英语的 101 键盘,日语的 106 键盘,韩语的 103 键盘等等)。对应到浏览器平台,并没有 scan code 的概念,而是通过 code 属性[4]表达类似的功能。
按照 W3C 组织的标准[6]定义,code 属性包含了用户所按物理键的信息。既然是物理键,那么就跟键盘的类型有关系,比如下图就是一个典型的英文 101-键盘的布局。
这里还有一个典型的日语的 106 键盘布局,可以看到其中多了一些日语输入相关的功能键。
无论是什么样的键盘布局,上面键的类型总可以分成那么几类。以字母数字(Alphanumeric)区域为例,常见的字母和数字键就可以称为书写系统(Writing System)键,而像 ALT、SHIFT 和 KanaMode 这样的就是功能(Functional)键。但无论哪种类型的键,都有一个唯一的名称或者 ID,这个就是 code 属性的值。比如下图中的“KeyA”就是字母 A 键产生的按键事件的 code 属性值,不管你使用的是什么键盘布局,这个 code 值是不会变的,因为它表示的是键的位置,而不是产生什么输出。
另外需要注意的是,图中紫色的键是任何键盘布局都有的键,而绿色的键是只有特定布局的键盘才有。比如回车键左边的“BackSlash”键,在英文键盘上就不存在。通过这个 code 属性,我们不仅能知道哪个键被按下了,还能了解一些客户端目前键盘布局的信息。比如你收到了一个值为“KanaMode”的 code 属性,那使用的键盘布局一定是日语键盘,因为其他类型的键盘不可能产生这个属性值。
其他属性
按键事件中还有一些常用的属性,比如与组合键相关的altKey
、ctrlKey
和shiftKey
,它们表示某个键被按下时,是否同时按住了某个功能键。这在处理一些快捷键相关逻辑时比较有用。例如处理 ALT+F 的组合,可以监听 keydown 事件,如果是F
键被按下,同时检查它的 altKey 属性,如果为true
,该组合键就生效了。
location 属性表示按键所在的区域,比如对于 ALT 键来说,如果是左边的 ALT,该值为1
;如果是右边,则为2
。如果涉及小键盘区的键,取值为3
也有可能。有些情况下这个属性也有用,比如 Windows 韩语输入法中,右边的 CTRL 键是作为HanjaMode
key 使用的,除了通过 code 属性区分,location 也可以。而在某些欧洲语言键盘布局中,右边的 ALT 键是AltGraph
键,在判断 keyCode 的同时,通过 location 也可以与左边的 ALT 区分开来。
repeat 表示某个键是否被一直按着,当长按某个键时触发。按多久算长按,每个系统平台可能有所区别,只有平台认为长按触发,该属性才是true
。
总结
本文介绍了浏览器中针对键盘事件的知识和注意事项,每个事件和属性的具体表现还要根据具体开发实践而定,标准写了不代表每个平台和浏览器都正确实现了,在开发时要勤查文档,但也不能迷信标准。从下篇文章开始,我们来看看输入法相关的事件。
参考阅读
[1] Keydown Event
[2] KeyCode Property
[3] Key property
[4] Code property
[5] Dead Key
[6] UI-Event Code
[7] Keypress Event
版权声明: 本文为 InfoQ 作者【天择】的原创文章。
原文链接:【http://xie.infoq.cn/article/a9df8e4c86c1eafee59de1c7d】。文章转载请联系作者。
评论