代码改变世界

使用中文输入法时对键盘事件的处理

2013-07-25 23:09  Owen Chen  阅读(15646)  评论(4编辑  收藏

最近很久没有更新博客了,不是没有东西写,而是没有时间写。公司项目上事情比较多,又在工会谋了份差事;家里房子装修,尽管有老爸盯着,但很多时候还是要自己跑来跑去。所以有时候有了写博客的想法,却老是坐不下来细细写。这些就算为自己这段时间的荒废找个小小的借口吧。

其实最主要的问题还是在于自己对博客的定位。之前一直想每篇博客都尽量找到一些比较好的主题,写的比较详细,这样看起来比较专业。但是这样要求的话,每一篇就要花狠多时间准备,很多时间编写,无形中也给自己带来了很多压力。这也是久久没有动笔的一个主要原因。后来在看很多牛人的博客的时候,发现他们也经常放一些小的但是很有用的东西在博客上。都是平时开发的时候遇到的小问题和解决的过程。这样的记录其实并不需要花太多的时间,而且能够巩固自己对知识点的掌握,也便于以后再次查阅。于是我觉得这样的方式挺好的,尤其是比较忙的时候,这样短小精炼的博客,同样能够给自己带来不少的长进,也能促进自己勤写博客的习惯养成。

闲话就讲到这里,接下来就进入今天的主题:使用中文输入法时,对键盘事件的处理。

问题的起源是这样的:组里有个同事发现现有项目上的一个自动补全的输入框不响应中文输入。英文输入的时候,超过两个字就开始向后台查询复合条件的选项。但中文输入的时候,在输入法上不管输入多少字,输入框没有任何反应,按了空格或者回车后,字进入了输入框,依然没有能够出发补全的功能。 于是这个问题就到了”前端专家“我的手里。

如何入手呢,一开始当然是看现象。 发现中文输入的时候,打出来的字都是在输入法的框里,于是猜想键盘事件都被输入法给截获了。但让我不解的是,为什么当字被输入到输入框里,还是没有反映。我第一反映是到google看输入框。于是分别打开chrome,firefox,ie看现象。

首先是Chrome,表现是最完美的,在输入框中输入中文的时候,输入的拼音在中文被输入之前填写在输入框里,匹配的内容应该是根据输入框里的英文字母进行匹配,所以令人很满意。

其次到了firefox,从下图中我们可以看到。当打开中文输入法打字的时候,并没有任何文字被输入到输入框中,所以也就没有任何匹配项出现。之后当按了回车或者空格,将文字输入到输入框中之后,才会有响应的匹配项显示。

再来看一下IE,结果和firefox一样,输入法打开的时候,也没有内容被捕获和匹配。

看完这三个浏览器的现象,大概证实了自己的猜想,输入框捕获了一些键盘事件,从而导致浏览器无法获取用户输入进行匹配。但当内容被填入输入框的时候,匹配被触发。 接下来一个很自然的想法,是不是onchange事件出发了匹配函数。于是自己开始动手试验:

写了一个最简单的例子,input box分别绑定onchange,onkeydown,onkeyup,onkeypress事件,查看事件被出发的情况:

 1 <html>
 2 <head>
 3     <script type="text/javascript">
 4         function onChange(e){
 5             console.log("onchange!");
 6         }
 7         function onKeyDown(e){
 8             console.log("onkeydown");
 9         }
10         function onKeyUp(e){
11             console.log("onkeyup");
12         }
13         function onKeyPress(e){
14             console.log("onkeypress");
15         }
16     </script>
17 
18 </head>
19 
20 <body>
21     <input type="text" onchange="onChange()" onkeydown="onKeyDown()" onkeyup="onKeyUp()" onkeypress="onKeyPress()"/>
22 </body>
23 </html>

然后开始用各个浏览器进行测试:首先是英文输入状态下

  普通按键 abc alt.shift,ctrl ESC Backspace Enter  
Chrome onkeydown,
onkeypress,
onkeyup
onkeydown,
onkeyup
onkeydown,
onkeyup
onkeydown,
onkeyup

onkeydown,
onkeypress,(当value发生变化的时候)
onchange,
onkeyup

 
Firefox onkeydown,
onkeypress,
onkeyup
onkeydown,
onkeyup
onkeydown,
onkeypress,
onkeyup
onkeydown,
onkeypress,
onkeyup
onkeydown,
onkeypress,(当value发生变化的时候)
onchange,
onkeyup
 
IE10 onkeydown,
onkeypress,
onkeyup
onkeydown,
onkeyup
onkeydown,
onkeypress,
onkeyup
onkeydown,
onkeyup
onkeydown,
onkeypress,
onkeyup
 

从上面的表格中可以看出,不同的浏览器对不同的按键触发的事件还是有所差别的。尤其是IE,按回车的时候不会出发onchange事件。

此外所有的浏览器,在onblur的时候,如果input的value发生了变化,都会触发onchange事件。但是在输入的过程中,onchange事件并不回被触发,于是否定了我自己的对于onchange事件触发查询的猜想。

接下来是在使用中文输入法的时候触发的事件列表

  普通输入abc 空格,填入内容  
Chrome 每次按键
onkeydown,
onkeyup
onkeydown,
onkeyup
 
Firefox 只在第一次按键时触发
onkeydown
onkeyup  
IE10 每次按键
onkeydown,
onkeyup
onkeydown,
onkeyup
 
       

从上面的表中可以看出,如果想在用户输入过程中,通过onkeydown或者onkeyup事件来捕获用户的输入,并且触发匹配调用是不太可行的,尤其是在firefox上,用户的键盘事件根本没有办法立刻获取。所以我们也就可以理解,为什么google的搜索框,在firefox中,也只能等待中文内容被天津输入框之后才能进行匹配搜索。

但是对于IE,依然能够获得keydown和keyup事件,那就够获得keycode,自然研究能够知道用户按了什么键,通过用户按键进行匹配应该也是可以做到的。为什么ie上对中文输入的反映与firefox是一样的呢。

继续搜索才发现,原来在中文输入框的情况下,键盘事件并不能传递真实的用户按键keycode。于是对代码进行修改,加入了keycode和charcode的输出:这里还遇到了firefox和ie对event参数的兼容性问题,前台开发真是不易啊。

 1 <html>
 2 <head>
 3     <script type="text/javascript">
 4         function onChange(){
 5             console.log("onchange!");
 6         }
 7         function onKeyDown(e){
 8             e = e || window.event;
 9             console.log("onkeydown" + " keyCode:"+e.keyCode+" charCode:"+e.charCode);
10         }
11         function onKeyUp(e){
12             e = e || window.vent;
13             console.log("onkeyup" + " keyCode:"+e.keyCode+" charCode:"+e.charCode);
14         }
15         function onKeyPress(e){
16             e = e || window.event;
17             console.log("onkeypress" +" keyCode:"+e.keyCode+" charCode:"+e.charCode);
18         }
19     </script>
20 
21 </head>
22 
23 <body>
24     <input type="text" onchange="onChange()" onkeydown="onKeyDown(event)" onkeyup="onKeyUp(event)" onkeypress="onKeyPress(event)"/>
25 </body>
26 </html>

各个浏览器的测试结果如下:

  a ESC 中文输入法a 中文输入法接着按b 空格,填入中文内容    
Chrome
onkeydown keyCode:65 charCode:0
onkeypress keyCode:97 charCode:97
onkeyup keyCode:65 charCode:0
onkeydown keyCode:27 charCode:0
onkeyup keyCode:27 charCode:0
onkeydown keyCode:229 charCode:0
onkeyup keyCode:65 charCode:0
onkeydown keyCode:229 charCode:0
onkeyup keyCode:66 charCode:0
onkeydown keyCode:229 charCode:0
onkeyup keyCode:32 charCode:0
   
Firefox
onkeydown keyCode:65 charCode:0
onkeypress keyCode:0 charCode:97
onkeyup keyCode:65 charCode:0
onkeydown keyCode:27 charCode:0
onkeypress keyCode:27 charCode:0
onkeyup keyCode:27 charCode:0
onkeydown keyCode:0 charCode:0 没有事件 onkeyup keyCode:32 charCode:0    
IE10 onkeydown keyCode:65 charCode:0  
onkeypress keyCode:97 charCode:97  
onkeyup keyCode:65 charCode:0 

onkeydown keyCode:27 charCode:0
onkeypress keyCode:27 charCode:27
onkeyup keyCode:27 charCode:0

onkeydown keyCode:229 charCode:0
onkeyup keyCode:65 charCode:0

onkeydown keyCode:229 charCode:0
onkeyup keyCode:66 charCode:0

onkeydown keyCode:229 charCode:0
onkeyup keyCode:32 charCode:0

   

 这里有人可能会对keyCode和charCode产生好奇。我也是写这篇博客的时候才了解到这个知识点:keyCode表示按下键的数字代码,也叫键码,charcode表示按键的Unicode,也叫字符编码。这里有一篇文章介绍都比较详细,大家可以具体去看一下:http://blog.sina.com.cn/s/blog_65c2ec5e0101blj6.html

 本文的重点是中文输入,大家可以看到,在chrome和ie中,使用中文输入法输入的时候,所有的keydown事件,keycode都是229,keyup事件的keycode表示正确。因此通过对keyup事件的捕获,应该能获取用户的按键情况。但是对于Firefox,只能等到用户按空格或回车将输入的内容填入框中,才能捕获keyup事件。然后通过输入的内容来进行匹配查询。

所以这个中文输入法自动匹配问题的最终解决方案也比较明显了:捕获keyup事件,然后通过输入框里的内容进行匹配查询。

查看原有的code,果然只对keydown和keypress(针对opera)事件进行了捕获,而没有用到keyup,所以修改的内容就是将keydown事件改为keyup事件。

 

一个小小的中文输入问题需要这么多的搜索和调试才能完美解决,而且这里只对chrome,firefox,ie做了支持,如果需要加上opera,safari,需要的工作可能更多。但相信这些问题碰到一个少一个,记录下来也希望能为别人带来一些便利。同时也盼望着将来各浏览器厂商之间规范的统一和相互兼容,让前端工程师能有更加顺畅的开发过程。