0%

Web 键盘输入法应用开发指南——基本概念

介绍

在Web应用中,用户交互是十分基本和重要的功能,而来自用户的输入又离不开键盘。现代Web浏览器已经在底层对键盘做了相对较好的支持(逐渐就能看出为什么是“相对”),并通过API的方式暴露给开发者。这包括针对按键的事件捕获,不同语言键盘布局(Keyboard Layout)的支持,对输入法的支持(涉及中、日、韩等东亚语言),以及对不同平台(Windows、Mac、iOS等)上组合键的支持,等等。在这个系列的开头,我们来过一遍浏览器提供的这些与键盘输入相关的基本技术特性,先有一个基本印象,后续再逐渐展开。

按键事件

对于按键事件(Keyboard Event)[1]大家应该都不陌生,在开发Web应用时或多或少应该接触过。一般来说,这类事件是你在键盘上按下某个键时最先触发的事件。“最先”的意思是还有后续的事件类型产生(比如input事件等),用于进一步的输入逻辑处理。而到这一步,你(或者说Web应用)能知道的是,浏览器从操作系统那里得到的按键信息。至于说这个按键会得到怎样的输入和文本,那就是后续事件的事情了。我前面还加上了“一般来说”,这是因为不同的浏览器,针对不同的输入场景(是否使用输入法),还有不同的实现。

浏览器将按键事件分为三类:keydown、keyup 和 keypress,通过Keyboard Event对象的type属性来区分。前两类的含义可以从字面理解,而keypress类型一般是针对产生具体字符的按键(比如单按一个Ctrl键就不会产生字符),但已不推荐使用了[2]。有了这些类型,就可以在应用中对用户的键盘输入做比较精细的处理。一个简单的例子是,我要知道用户按住了某个特定的键,持续一段时间,以执行某个业务逻辑,并在释放按键时终止逻辑。

按键事件对象里还有一些常用的属性,比如code属性告诉你哪个键被按了,location属性告诉你这个键在键盘的哪个区域(是左Alt还是右Alt),isCompsing属性告诉你这个键是不是在使用输入法期间按下的,等等。当然,这些属性很多都有兼容性问题,我会在后面细说。

键盘布局

简单来说,键盘布局[3](Keyboard Layout)规定了你的键盘上某个位置的键将会产生怎样的输出,这是一个映射关系。比如在Windows系统上,我们输入中文和英文,其实都在使用英文键盘布局,而输入日文和德文就需要相应的日文和德文键盘布局了。在英文布局上,你按下“y”这个键,结果就输出“y”这个字符,而在德文布局下,同样的键会产生“z”字符。键盘布局在你切换输入语言或者输入法时会进行相应变换,这些由系统完成。

kbime-switch

系统键盘/输入法切换

在一些系统上有Scan Code的概念,一个键的scan code标识了这个键在当前所使用键盘上的物理位置。比如在常用的英文键盘上,左边shift键旁边的位置就是字母“z“,那么它的scan code就标识了这个键的位置在左shift的右边,仅此而已。至于按下键后会输出什么字符,这就需要键盘布局的信息了。系统会根据当前的键盘布局,将来自物理键盘的scan code信息“翻译”成应该输出的字符。

de-keyboard

德语键盘布局

现代浏览器的实现中没有直接拿到scan code,但通过键盘事件中的code属性[4],可以访问到类似的位置信息。例如按下字母“Q”键产生的keydown事件中code属性的值为KeyQ,它的含义是英文键盘上对应Q的位置的键被按下了。注意IE浏览器不支持code属性,在开发时要考虑用户的场景。在这里[4]可以找到更详细的针对不通过键盘类型的code定义。

那么浏览器中的应用能知道用户当前的系统键盘布局吗?答案是:目前还不能。因为这需要浏览器通过系统API得到需要的信息,而浏览器还没有提供JavaScript接口。目前只有个getLayoutMap接口[5],且还没有被所有主流浏览器支持。它返回一个promise,并在promise被resolve时拿到一个从按键信息到字符信息的map。这样只要通过事件对象拿到按键信息(比如code属性),就可以得到它在当前系统键盘布局下的输出字符,从而可以反推目前系统的键盘布局信息。当然,如此获得键盘布局信息还是略显笨拙且容易出错,还是期待未来标准中对键盘信息有更为完善的支持。

输入法事件

对于中、日、韩等东亚语言来说,输入法(Input Method Editor)是必不可少的输入工具。浏览器对输入法需要特别支持,原因在于使用输入法时有一个组字(Composition)的过程。这一点我们很熟悉了,比如用中文输入法打出“你好”两个字,你需要先输入“nihao”的拼音,然后从候选框中选择你要输入的字或者词。对于浏览器来说,最终填入输入框的是“你好”两个字,但是前面输入拼音的过程也需要精确地监控,以满足可能的业务需求。

浏览器中与输入法相关的常用事件[6]有,compositionstart(开始组字),compositionupdate(更新组字),compositionend(结束组字)等。有了这些事件,应用程序可以知道用户是否在使用输入法输入,目前到了组字的哪个阶段,以及是否提交了组字结果。对于日文输入法来说,还有分段组字的支持,可以对不同词语调出候选框进行选择。此外,input事件有时也很有用,可以根据input事件的不同类型[7],来获取当前的输入内容或者进行其他操作。

需要注意的是,不同浏览器对各类输入法事件的支持并不一致,W3C组织制定的相应标准也有多个版本(Level1,Level2…),而不同浏览器厂商对标准的实现有出入,涉及触发事件的类型事件的时序以及兼容性等。这就需要开发者了解多种浏览器的实现,避免出现与输入相关的bug。

组合键

浏览器对组合键也需要特别的支持,否则用户的输入将无法正确实现。组合键可以大致分为两类,一类是功能性的,比如Ctrl+Shift+字母键,在某个应用中完成特定的功能;另一类是辅助输入的,比如在西班牙语等欧洲语言中,要想打出特定的字符,需要先按某个功能键,再按一个字母键,才能得出正确结果,比如字符á,它需要先按英语键盘上的[键,再按字母a键。浏览器需要一些特殊的按键事件属性对这些输入提供支持。

对于第一类组合键,应用程序需要知道当某个键被按下时,是否还有某个功能键(如Alt,Ctrl,或者Shift等)也被按下了,这时可以通过keydown事件的*Key属性来判断。比如,如果是Alt+F组合键,那么在F的keydown事件中,altKey属性值为true;如果是Ctrl+Shift+K,那么在K的keydown事件中,ctrlKeyshiftKey属性都为true

对于第二类组合键,比如字符西语字符á,先按的[键被称为Dead Key[8],此时浏览器产生了keydown事件,但没有产生任何输出,事件的key属性值为Dead。当字母键被按下时,产生正确的输出,同时key属性的值被设置为最终的字符á

组合键的问题有时候会很复杂,因为一些组合键可能先被系统的输入法或者其他进程捕获,而没有发送到应用程序。还有些时候,为了防止组合键的副作用,需要显式地阻断一些组合键的效果,这都需要对按键事件进行特殊处理,同时有不能影响正常的用户输入。

总结

本文梳理了Web浏览器中键盘和输入法功能的要点,可以让你对基本的概念有一些感性的印象,从下篇文章开始,我们开始逐一探讨具体的开发技术。欢迎关注和讨论。

参考阅读