上面是谷歌首页,当输入某个词时,下面会弹出自动补全提示。功能并不复杂,但考虑到要兼容各个浏览器,谷歌为此可能花费了不少功夫。然而完美实在是很难得,谷歌首页上一直有一个处理得不妥的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,在输 入法未开启时,给用户更友好的体验。
最后,看一个根据方案三来实现的简单例子:表单输入时的即时校验
参考资料
- 很让人敬佩的鼠标事件总结:JavaScript Madness: Keyboard Events
- Realazy的探索:输入法下keyup失效的解决方案