如何打造完美的表单即时校验功能
为了讨论这个问题,我们得先看看各个浏览器下,在输入法开启和未开启时,对键盘等事件的响应情况。
[测试环境说明]
系统:Windows Vista Ultimate SP1
输入法:谷歌拼音输入法 1.2.30.71
IE版本:ie6, ie7, ie8 beta1
Firefox版本:2.0.0.16, 3.0.1
Safari版本:3.1.2
Opera版本:9.51
[键盘事件测试小结]
[Firefox]
一、输入法未开启时,
1. 正常按下再放起A键,会依次触发keydown, keypress, input, keyup
2. 仅按下修饰键(Ctrl/Shift/Alt)时,不会触发keypress. 注意:按下Esc, Insert, Tab, Pause, Left, Up, Enter等键时,也会触发keypress. 小结:keypress在按下非字符键时,有些会触发,有些不会触发,具体浏览器还不同。
3. 仅在输入框的值有变化时,才会触发input. 比如Backspace键会引起值的变化,因此会触发input. 但是要注意:光标在输入值最后面时,按下Delete键不会改变输入值,但依旧会触发input.
4. 按住某键不放时,会连续触发keydown. 当按下的是会触发keypress的键(参考上面第2条)时候,还会连续触发keypress. 同样,如果按下的是会触发input的键,也会连续触发input.
5. Ctrl+C/V/X快捷键粘贴复制等操作时,会依次触发keydown, keydown, keypress, input, keyup, keyup. 其中input仅在值有变化时才触发(比如粘贴一张图片到文字输入框时,不会触发input)。
6. Ctrl+Z/Y操作时,和第5条规律一致。
7. 通过鼠标右键进行粘贴复制等操作时,仅会触发input.
二、输入法开启时,
1. 输入法中输入第一个字符时,触发keydown, keypress. 输入过程中不会触发键盘事件,输入完成时,会触发input, input, keyup.
2. 输入法开启时,输入数字时,和输入法未开启时一致。
[IE]
一、输入法未开启时,
和Firefox基本一致,就是input改成propertychange.
注意:
2. 在ie中,仅字符键会触发keypress. 这比firefox强。
3. 在ie中,Esc和Backspace功能一样,这和firefox不同。在ie中,Delete键没有改变值时,不会触发propertychange.
5. 在ie中,Ctrl+C/X/V不会触发keypress.
6. 在ie中,如果监听的事件中YAHOO.log这种页面输出的语句时,会导致Ctrl+Z/Y失效。没有输入语句时不会。(这可以认为是IE的Bug)
二、输入法开启时,
1. 输入法中输入第一个字符时,触发keydown, keyup. 输入过程中也会连续触发keydown, keyup. 输入完成时,触发keydown, propertychange, keyup. (无论怎样都不会触发keypress)
2. 输入法开启时,输入数字时,和输入法未开启时一样,但不会触发keypress.
[Safari]
一、输入法未开启时,
大部分和Firefox一致。注意以下几条:
2. 和IE类似,仅字符键会触发keypress.
5. 和IE一致。
二、输入法开启时,
1. 输入法中输入第一个字符时,触发keydown, input, keyup. 输入过程中会连续触发keydown, input, input, keyup. 输入完成时,触发keydown, input, input, keyup. (和IE一样,不会触发keypress)
2. 输入法开启时,输入数字时,和IE一样,不会触发keypress.
[Opera]
一、输入法未开启时,
大部分和Firefox一致。注意以下几条:
2. 所有键都会触发keypress.
4. 连续按下某键时,仅会连续触发keypress. (这个处理比其它浏览器合理)
5. 多个keypress.
二、输入法开启时,
和上面的浏览器都不同,仅在输入完成时触发input.
[解决方案]
考虑到以上各种情况,实现表单的即时校验和自动补全完成等功能时,可以考虑的方案有:
一、监听keydown, keypress, keyup事件来实现。这是大部分JavaScript教科书里的做法,对于没有输入法的拉丁语系国家,基本没有任何问题。但输入法一开启后,一切就都不美妙了:
1. 无论有没有输入法,通过鼠标右键粘贴复制时,key事件都触发不了,真糟糕。
2. 输入法开启时,各个浏览器表现不同。不同输入法还有不同,太让人沮丧了。
3. 输入法开启时,Opera压根不认。亲爱的Opera,叫我怎么爱上你?
考虑到中文用户,特别是第一条(发现不少中文用户习惯使用右键菜单来粘贴复制,使用频率还蛮高的),第一个方案基本可以枪毙掉。
二. 不考虑这些烦人的键盘事件,直接采用定时器的方法,在输入框获取焦点时,触发定时器,失去焦点时,关闭定时器。定时器每隔200ms检查输入框的值,根据值的变化来进行下一步操作。这个trick很简单,但基本上能解决大部分问题了(Google首页的自动完成就是这么做的^o^)。但有以下不妥:
1. 性能问题。(因为仅在获取焦点时触发定时器,性能问题倒是可以不考虑的)
2. ie下,此方法会导致Ctrl+Z/Y快捷键失效。(Google也有这个Bug)或许这是一个可以忽略的小Bug.
3. 用定时器实现表单的输入时的动态校验时,当输入法开启时,如果校验函数在输入未完成时改变输入框的值,会导致输入框的值变成空。(这个输入不可忽视的Bug,很恼人)
第二个方案,自动解决了右键菜单粘贴的问题,对于自动补全完成功能来说,足够用了。做动态校验时,不考虑Firefox下的输入法用户时,也完全可行。
三、在上面的浏览器事件测试中,有一个很让人高兴的发现:所有浏览器中,都会触发input(ie下是propertychange)事件。而且input事件仅在输入框的值有变化时才会触发。在输入法开启时,input也能正常触发(虽然会触发一些冗余的input,但比起定时器来好很多)。在右键菜单粘贴等操作时,也能正确触发input. 采用input/propertychange, 几近完美。目前发现的问题是:
1. ie下,如果监听的事件函数中,有页面输出操作,如YAHOO.log(...), 会导致Ctrl+Z/Y失效。
2. 对于表单输入时的动态校验来说,比如只能输入数字的输入框,当输入字母时,监听input事件的实现会在字母显示出来后,立刻又删除掉(和keyup的实现效果一样),没有监听keypress的效果好(不会先显示出来)。因此对于动态校验来说,可以结合input和keypress,在输入法未开启时,给用户更友好的体验。
最后,看一个根据第三种方案的实现范例:表单即时校验输入框的一个简单范例
[参考资料]
很让人敬佩的鼠标事件总结:http://unixpapa.com/js/key.html
Realazy的探索:http://realazy.org/blog/2007/10/31/solution-of-keyup-failing-when-ime-is-on/