HTML5 number input 踩坑记

作者: 1024译站 | 来源:发表于2017-01-04 10:23 被阅读4768次

    众所周知,HTML5的input 新增了很多类型,如 number,email,url,date系列,search,tel等等。这些新类型提供了更好的输入控制和验证。然而,在使用的过程中你会发现,不该对它们期望过高。其中的 number 类型比较常用,最近项目里也用到了它,过程中碰到不少麻烦,算是用hack的方式解决了,特记此文,希望对大家有所帮助。

    产品的需求是,输入框只能输入数字和小数点,并且整数位数不超过9,小数位数不超过2。通常首先想到的是用<input type="number"/>,因为语义上它是数字输入框,而且在移动端获得焦点时默认弹出数字键盘。看上去很美好,但这跟需求还差不少距离。

    • 没有长度限制
    • 它可以输入除数字和点号以外的字符

    关于长度限制,你可能想到了maxlength属性,但遗憾的是它对 number类型无效。况且即便能用maxlength,也很难实现前面的需求,因为小数部分是可选的。至于字符限制,你可能会想到用键盘事件来拦截。没错,可以监听keydown事件,判断按键的keyCode 来拦截无效字符:

    var input = document.querySelector('input');
    var validKeys = [];//合法的按键列表
    input.addEventListener('keydown', function(e) {
        if(validKeys.indexOf(e.keyCode) < 0) {
            e.preventDefault();
        }
    });
    

    这样就无法输入非法字符了。然而现实是残酷的,PC上可能工作良好,但移动端浏览器对键盘事件支持并不是很好,Android webview 里取到的keyCode全都是0。这是chrome早几年的bug,到现在都没完全解决。参考这里(需翻墙)。是不是很心塞?
    别灰心,还有其他办法。比如,监听input事件,在其value变化的时候检测字符串。

    var input = document.querySelector('input');
    var validKeys = [];//合法的按键列表
    input.addEventListener('input', function(e) {
        if(e.target.value === '') {
            input.value = oldValue;//伪代码,重置为上次有效值
        }
    });
    

    <input type="number" />有个特性,只要输入框内的字符串不能转换成浮点数,它的值就变为空了。我们可以利用这个特性,发现输入非法,就将输入值撤回。但是问题又来了,当你按退格键删除字符时,由于前面的逻辑,最后一个字符无法删除。这是因为无法区分是退格键还是输入了非法字符导致值为空,都重置为上次有效值了。结合键盘事件可以区分,但是前面说了,移动端不靠谱。既然number input 有诸多限制,为什么不直接用 text input呢?没错,text input可以避免这个问题,但是它的默认键盘不是数字键盘。还有个选择,用<input type="tel" />,它有text input的所有特性,还能拥有默认数字键盘。燃鹅,在iOS里是拨号键盘,没有点号。

    <input type="tel" />

    我只是想输个数字,咋这么多坑?!

    稍安勿躁,终极大招来了。咱们还是继续用number input。它还有个不常用的事件,叫textInput。这是DOM LEVEL3 的事件,但实际上w3c标准里已经删掉它了,不过验证发现chrome和safari都支持。它可以监听到任何输入,这样就可以区分是输入了非法字符还是按了退格键。

    var input = document.querySelector('input');
    var validKeys = [];//合法的按键列表
    input.addEventListener('textInput', function(e) {
        if(isNaN(e.data)) {
            input.value = oldValue;//伪代码,重置为上次有效值
        }
    });
    

    至此,基本解决了需求里的所有问题。
    注意,以上方案只是为了在移动端webview里解决问题,并且是hack的方式,并非完全可靠。代码也省略了很多细节。如果大家有更好的办法,欢迎交流。

    参考资料:
    https://bugs.chromium.org/p/chromium/issues/detail?id=118639#c85
    https://www.w3.org/TR/DOM-Level-3-Events/#beforeinput
    https://www.filamentgroup.com/lab/type-number.html

    相关文章

      网友评论

      • 5338f461ad61:傻逼产品
      • Jeepin:当我用输入的时候是没问题的,可以输入1.0,但是提交数据到后台的时候,input还是把它转换成了1,关键的问题就是不管是输入,还是手动点击增加,变整数的时候不会补充小数点和0。
      • Jeepin:请问type=“number” 中我设置step为小数,如0.5,点击箭头增加数字,当增加到1的时候,这个时候是显示为1,而不是1.0。这样会出现抖动问题,为了更好的视觉感,我绝觉得这个需求还是有必要的。结果搜索了很久,也没找到合适的方法。那么楼主有没有什么好的建议或者方法。在此谢过。
      • 道坤_5f91:type=“number” 配合step/min/max还是可以解决大部分问题,但是遇到国际化就抓瞎了
        不知道楼主有没有好方法啊?
        1024译站:可以在输入的时候用number input,显示的时候切换成text input
      • 1e0178bafb49:谢谢楼主,很有用。
        之前用是用 input 监听 event.target.validity.badInput 来判断是否为非法输入。IE9 可以直接获取 value,其他浏览器可以用 badInput 判断。
        但是有个坑, 输入 0 开头的,input 监听不到。
      • 我是一坨:限制输入位数,我是用另一种方法实现的。貌似比笔者的好用
        1024译站:欢迎分享
      • 我是一坨:微信浏览器内打开,怎么消除那句提示:自动填充当前内容?
        我是一坨:@一毕业就要15k 就是数字input框的页面。获得焦点的时候,你在微信浏览器看看
        1024译站:打开什么?
      • 庄引之:input type="number"可以设置最大值属性max为999999999实现 整数位数不超过9
      • 㱎䖘䵈䶁䘔䶑䘓鋱䩳䵷㒪䪉䉥:好东西 ,最近刚好碰见了这个问题 。
        1024译站:很高兴能对你有所帮助:smile:
      • 鹤庭逸:楼主真是舍得研究,换成我就直接text了
        1024译站:没办法啊,产品需求要完成嘛

      本文标题:HTML5 number input 踩坑记

      本文链接:https://www.haomeiwen.com/subject/oisnvttx.html