论坛首页 Web前端技术论坛

从谷歌的一个Bug说起,谈谈键盘事件的兼容性

浏览 21103 次
该帖已经被评为精华帖
作者 正文
   发表时间:2008-08-26  

点击小图看大图

上面是谷歌首页,当输入某个词时,下面会弹出自动补全提示。功能并不复杂,但考虑到要兼容各个浏览器,谷歌为此可能花费了不少功夫。然而完美实在是很难得,谷歌首页上一直有一个处理得不妥的Bug:

除了Ctrl+X/C/V,Ctrl+Z/Y也是经常使用的快捷键。在Firefox/Safari/Opera中访问谷歌,一切都很正常。但当我们打开IE浏览谷歌时,Ctrl+Z/Y不起作用了。在当今特别是中国,IE是份量最重的浏览器,谷歌首页有此问题,完全可以归为一个Bug.

这个Bug究竟是怎么引起的呢?为了进一步讨论,我们先来看看各个浏览器下,当输入法开启和未开启时,对键盘等事件的响应情况。

猛击查看 JavaScript键盘事件测试小结

通过上面这篇测试小结,我们可以看出,要实现谷歌的自动补全功能并不是一件很简单的事。目前可以找到的解决方案有:

方案一:通过监听keydown, keypress, keyup事件来实现。这是大部分JavaScript教科书里的做法,对于拉丁语系国家,是没有问题的。但输入法一开启后,一切就都不美妙了:

  1. 无论有没有输入法,通过鼠标右键粘贴复制时,key事件都触发不了,真糟糕。
  2. 输入法开启时,各个浏览器表现不同。不同输入法之间还有差异,太让人沮丧了。
  3. 输入法开启时,Opera压根不触发键盘事件。亲爱的Opera,叫我怎么爱上你?

考虑到中文用户,特别是第一条(不少中文用户习惯使用右键菜单来粘贴复制),第一个方案基本可以枪毙掉。

方案二:不考虑这些烦人的键盘事件,直接采用定时器来实现。在输入框获取焦点时,触发定时器,失去焦点时,关闭定时器。定时器每隔200ms检查输入框的值,根据值的变化来进行下一步响应。这个trick很简单,基本上能解决大部分问题(谷歌的自动完成就是这么做的^o^)。但有以下不妥:

  1. 性能问题。(因为仅在获取焦点时触发定时器,性能问题倒是可以不考虑的)
  2. ie下,此方法会导致Ctrl+Z/Y快捷键失效。
  3. firefox下,用定时器实现表单输入时的即时校验,当输入法开启时,如果校验函数在输入未完成时改变输入框的值,会导致输入框的值变成空。(这个属于不可忽视的Bug,很恼人)

方案二自动解决了右键菜单粘贴的问题,对于自动补全功能来说,一般应用场景下也足够用,但对谷歌来说,感觉有此Bug是不妥的。如果用方案二来实现表单输入时的即时校验,上面的第3点会导致firefox的中文用户很不爽。

苦思冥想许久,在键盘事件中折腾来折腾去,感觉怎么做都无法同时解决上面的所有不妥。昨天静下心来对所有A级浏览器的键盘事件在输入法开启和未开启时做了个仔细的测试后,下面这个方案就很清晰的浮了出来:

方案三:在上面的浏览器事件测试中,有一个很让人高兴的发现:所有浏览器中,都会触发input(ie下可以用propertychange)事件。而 且input事件仅在输入框的值有变化时才会触发。在输入法开启时,input也能正常触发(虽然会触发一些冗余的input,但比起定时器来好很多)。 在右键菜单粘贴等操作时,也能正确触发input. 采用input/propertychange, 几近完美。但需要注意以下几点:

  1. ie下,如果监听的事件函数中,有页面输出操作,如YAHOO.log(…), 会导致Ctrl+Z/Y失效。(深层次原因还需仔细探索,感觉页面输出操作只是表象)
  2. 对于表单输入时的即时校验来说,比如只能输入数字的输入框,当输入字母时,监听input事件的实现会在字母显示出来后,立刻又删除掉(和 keyup的实现效果一样),没有监听keypress的效果好(不会先显示出来)。因此对于即时校验来说,可以结合input和keypress,在输 入法未开启时,给用户更友好的体验。

最后,看一个根据方案三来实现的简单例子:表单输入时的即时校验

参考资料

   发表时间:2008-08-27  
写得非常好,很翔实

回复者寥寥啊。

第一时间参考它修改了自己的一些代码
0 请登录后投票
   发表时间:2008-08-27  
小处不可随便,严谨为编程之道
0 请登录后投票
   发表时间:2008-08-27  
稍微有点问题,比如第一个只能输入六个数字,但是我输入六个之后,选中输入的,应该是重新输入了吧,但是...呵呵
0 请登录后投票
   发表时间:2008-08-27  
剛才試了一下,果然是這樣的,佩服樓主的細心和鍥而不捨。
0 请登录后投票
   发表时间:2008-08-27  
shunzheheliu 写道
稍微有点问题,比如第一个只能输入六个数字,但是我输入六个之后,选中输入的,应该是重新输入了吧,但是...呵呵


的确有这个问题,看看去,谢谢仔细测试
0 请登录后投票
   发表时间:2008-08-28  
我一直使用的英文google,很少用中文,英文的谷歌没有发现该问题,呵呵
2 请登录后投票
   发表时间:2008-08-28  
2008-08-28补充:
1. oninput事件:a). 当脚本中改变value时,不会触发;b). 从浏览器的自动下拉提示中选取时,不会触发。
2. onpropertychange事件:当input设置为disable=true后,onpropertychange不会触发。

感谢各位朋友的测试与指正。

PS: shunzheheliu 提出的bug已经修复,keypress里面判断长度导致的。
1 请登录后投票
   发表时间:2008-08-29  
现在用AJAX做一个自动补全功能很简单,但是google的自动补全却很神奇,他的反应速度如此之快,是如何实现的?显然不是从数据库中读取的,似乎是从缓存读到的。有知道详情的朋友请透露一下。
0 请登录后投票
   发表时间:2008-08-31  
确实,为什么google的速度这么快呢
我就是在项目中从数据库中查几条数据的列表,都是loading一会
而google的几乎不需要时间,确实神奇
看似简单的gogole页面,隐藏着强大的技术,佩服
0 请登录后投票
论坛首页 Web前端技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics