从谷歌的一个Bug说起,谈谈键盘事件的兼容性
右图是谷歌首页,当输入某个词时,会弹出自动补全提示。功能并不复杂,但到要兼容各个浏览器,谷歌为此可能花费了不少功夫。然而完美实在是很难得,仔细测试可以发现一个处理得不妥的Bug:
除了Ctrl+X/C/V,Ctrl+Z/Y也是经常使用的快捷键。在Firefox/Safari/Opera中访问谷歌,一切都很正常。但当我们打开IE浏览谷歌时,Ctrl+Z/Y不起作用了。在当今特别是中国,IE是份量最重的浏览器,谷歌首页有此问题,完全可以归为一个Bug.
这个Bug究竟是怎么引起的呢?为了进一步讨论,我们先来看看各个浏览器下,当输入法开启和未开启时,对键盘等事件的响应情况。
猛击查看 JavaScript键盘事件测试小结
通过上面这篇测试小结,我们可以看出,要实现谷歌的自动补全功能并不是一件很简单的事。目前可以找到的解决方案有:
方案一:通过监听keydown, keypress, keyup事件来实现。这是大部分JavaScript教科书里的做法,对于拉丁语系国家,是没有问题的。但输入法一开启后,一切就都不美妙了:
- 无论有没有输入法,通过鼠标右键粘贴复制时,key事件都触发不了,真糟糕。
- 输入法开启时,各个浏览器表现不同。不同输入法之间还有差异,太让人沮丧了。
- 输入法开启时,Opera压根不触发键盘事件。亲爱的Opera,叫我怎么爱上你?
考虑到中文用户,特别是第一条(不少中文用户习惯使用右键菜单来粘贴复制),第一个方案基本可以枪毙掉。
方案二:不考虑这些烦人的键盘事件,直接采用定时器来实现。在输入框获取焦点时,触发定时器,失去焦点时,关闭定时器。定时器每隔200ms检查输入框的值,根据值的变化来进行下一步响应。这个trick很简单,基本上能解决大部分问题(谷歌的自动完成就是这么做的^o^)。但有以下不妥:
- 性能问题。(因为仅在获取焦点时触发定时器,性能问题倒是可以不考虑的)
- ie下,此方法会导致Ctrl+Z/Y快捷键失效。
- firefox下,用定时器实现表单输入时的即时校验,当输入法开启时,如果校验函数在输入未完成时改变输入框的值,会导致输入框的值变成空。(这个属于不可忽视的Bug,很恼人)
方案二自动解决了右键菜单粘贴的问题,对于自动补全功能来说,一般应用场景下也足够用,但对谷歌来说,感觉有此Bug是不妥的。如果用方案二来实现表单输入时的即时校验,上面的第3点会导致firefox的中文用户很不爽。
苦思冥想许久,在键盘事件中折腾来折腾去,感觉怎么做都无法同时解决上面的所有不妥。昨天静下心来对所有A级浏览器的键盘事件在输入法开启和未开启时做了个仔细的测试后,下面这个方案就很清晰的浮了出来:
方案三:在上面的浏览器事件测试中,有一个很让人高兴的发现:所有浏览器中,都会触发input(ie下可以用propertychange)事件。而且input事件仅在输入框的值有变化时才会触发。在输入法开启时,input也能正常触发(虽然会触发一些冗余的input,但比起定时器来好很多)。在右键菜单粘贴等操作时,也能正确触发input. 采用input/propertychange, 几近完美。但需要注意以下几点:
- ie下,如果监听的事件函数中,有页面输出操作,如YAHOO.log(…), 会导致Ctrl+Z/Y失效。(深层次原因还需仔细探索,感觉页面输出操作只是表象)
- 对于表单输入时的即时校验来说,比如只能输入数字的输入框,当输入字母时,监听input事件的实现会在字母显示出来后,立刻又删除掉(和keyup的实现效果一样),没有监听keypress的效果好(不会先显示出来)。因此对于即时校验来说,可以结合input和keypress,在输入法未开启时,给用户更友好的体验。
最后,看一个根据方案三来实现的简单例子:表单输入时的即时校验
2008-08-28补充:
- oninput事件:a). 当脚本中改变value时,不会触发;b). 从浏览器的自动下拉提示中选取时,不会触发。
- onpropertychange事件:当input设置为disable=true后,onpropertychange不会触发。
参考资料
- 很让人敬佩的鼠标事件总结:JavaScript Madness: Keyboard Events
- Realazy的探索:输入法下keyup失效的解决方案


August 27th, 2008 on 0:43
我一般是用
不知道这样是否 和谐?
August 27th, 2008 on 8:22
to cssrain: 用什么呢?被河蟹掉了?
August 27th, 2008 on 11:06
感謝分享,讓我增長一些見識!
August 27th, 2008 on 22:31
from asgardmoon@蓝色理想:
onpropertychange在IE下行为我觉得有很多异常行为,比如如果你修改了一个text类型的Input值,那么你每次刷新都会激活onpropertychange事件。
oninput则在用脚本改变其值的时候不能激活事件。
所以我仍然选择了计时器。
to sgardmoon:
第一个问题不大清楚,指的是这个帖子里的问题吗?
onPropertyChange onReset className bug IE6 & IE7 bug
onpropertychange在ie6以上版本,目前只发现当设置disabled=true时,不会触发,其它情况下没发现什么问题。(可能我运气好,暂时没遇到 )
通过脚本改变value时,的确不会触发oninput,但对于自动补全和即时校验来说,不触发反而是好的,避免了冗余事情。
August 27th, 2008 on 22:33
shunzheheliu@JavaEye 写道
稍微有点问题,比如第一个只能输入六个数字,但是我输入六个之后,选中输入的,应该是重新输入了吧,但是…呵呵
to shunzheheliu:
的确有这个bug,看看去。感谢反馈
August 28th, 2008 on 15:28
补充:
1. oninput事件:a). 当脚本中改变value时,不会触发;b). 从浏览器的自动下拉提示中选取时,不会触发。
2. onpropertychange事件:当input设置为disable=true后,onpropertychange不会触发。
April 3rd, 2009 on 10:29
onpropertychange很有问题
我一次用来做ajax检测
不知怎的不断触发弄死浏览器
July 18th, 2009 on 16:25
唉,佩服佩服。我前段时间也在做这个自动提示的组件,用方案一实现了就沾沾自喜。没想到你能研究的这么深。
July 20th, 2009 on 9:16
bsn.AutoSuggest 国外的这个不错
July 31st, 2009 on 7:07
我怎么没发现第二个方案的第三点问题,也就是firefox下用定时器实现的问题. 我在g.cn 上用firefox没发现有什么问题呀
July 31st, 2009 on 7:08
ctrl+z ctrl+y 的失效我觉得可以完全忽略不计. 好像到目前我在搜索框中使用此快捷键的机率太小了
January 11th, 2010 on 17:20
果真如此,到现在都还没解决,百度同样也是
June 3rd, 2010 on 18:26
onpropertychange很有问题
当我用上下箭头选择自动提示时,它会再次触发onpropertychange,
把选择的值再次提交返回查询的结果。这样自动提示没意义了。
而oninput就不会,好像input失去焦点就不再oninput了,而onpropertychange失不失去焦点都触发,,很烦人。还不能完美实现自动提示信息
June 3rd, 2010 on 18:32
比如我要选择自动提示第三条信息,当我用上下箭头选择时,刚滑过第一条,input值 改变了,会再次触发onpropertychange,又把选择第一条的值提交再查询返回自动提示信息。我选不了第二或第三条。oninput在火狐下却不是的。虽然input值改变了,但可以用上下箭头进行上下选择自动提示的信息。。
July 22nd, 2010 on 14:07
现在我遇到个问题,功能类似的提示,关键是input框是可以增加的,而每个增加的input框都要绑定keyup事件,在火狐下正常,但在ie下,新加的第一个正常,再加一个的时候就触发不了keyup事件了,郁闷。
leave a reply