likes
comments
collection
share

到底怎么让输入框只能输入数字(包括正数、负数和0)

作者站长头像
站长
· 阅读数 32

最近在做一个移动端输入框,表示成本,用户只能输入数字(包括正数、负数和0),想来就是限制用户输入内容,网上找了一圈,都没有完全符合我的要求,没有办法,只能自己硬着头皮看正则,debugger,一步步调整,改成能满足要求代码。

本文参考代码:el-input限制只能输入数字,且保留2位小数 这篇文章满足了限制用户输入正数,但是没有实现负数,在此基础上进行改造。

type=text

实现了:

  • 输入的非数字自动过滤掉,负号(-)小数点(.)不会被过滤
  • 以0开头,0后面是数字,会过滤掉前面的0,取后面的数字;以-0开头,0后面是数字,会过滤掉前面的0,取后面的数字
  • 负号(-)如果存在只能放在第一个
  • 小数点只保留第一个
  • 鼠标 blur 事件时,.89 这种形式补充为0.89-.89 这种形式补充为-0.89。数据保留2位小数。

先看代码结论:

interface InputNumberProps {
  value: string;
  onChange: (value: string) => void;
}

function InputNumber(props: InputNumberProps){
  const {value, onChange} = props;
  const handleChange = (e: any)=>{
    const value = e.target.value;
    const newValue =
      value
      .replace(/[^\d^\.^-]+/g, '')   // 把不是数字,不是小数点的过滤掉
      .replace(/^0+(\d)/, '$1')      // 以0开头,0后面为数字,则过滤掉,取后面的数字
      .replace(/^-0+(\d)/, '-$1')    // 以-0开头,0后面为数字,则过滤掉,取后面的数字
      .replace(/-/g, (match: any, offset: number) => offset === 0 ? '-' : '') // 只允许第一个是负号-
      .replace(/\./, '#').replace(/\./, '').replace(/#/, '\.') // 只保留第一个小数点
    //匹配负数,包括负整数和负小数 -xxx  -xxx.xx  
    const matchNegative = newValue.match(/^-\d*(\.?\d{0,2})/g)

    if (matchNegative) {
      return onChange(matchNegative[0])
    }

    //匹配正数,包括正整数和正小数 xx xx.xx  
    const matchPositive = newValue.match(/^\d*(\.?\d{0,2})/g)

    if (matchPositive) {
      return onChange(matchPositive[0])
    }
    onChange(newValue)
  }
  return (
      <input type="text" value={value} 
        onBlur={() => {
          const newValue = `${value}`.replace(/^\./, '0.')
          const resultValue = newValue.replace(/^-\./, '-0.')
          // 保留2位小数
          const fixedValue = Number(resultValue).toFixed(2)
          onChange(fixedValue)
        }}
        onInput={handleChange}
      />
  )
}

用到的正则:

^: 匹配输入字符串的开始位置,除非在方括号表达式中使用,当该符号在方括号表达式中使用时,表示不接受该方括号表达式中的字符集合。要匹配 ^ 字符本身,请使用 \^

\d: 匹配任意一个阿拉伯数字(0 到 9)。等价于 [0-9]

*: 匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 \*

+:匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 \+

?: 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使用 \?

修饰符:

gglobal - 全局匹配 | 查找所有的匹配项。

就用到这么多的正则语法,是不是比较简单,

input 类型 text, 受控组件通过 value 来控制显示的内容。change 事件可以一个一个字符识别到。如果 input 类型使用 input时,则 change 并不能完全识别到用户的输入。当用户输入的是非数字类型,change 事件得到的内容是空字符串,见下文:

type=number

因为是移动端h5页面,那用户 focus 到 input 框的时候,最好能弹出来数字键盘,而不是拼音键盘,以提高用户体验。这里用到 input 的一个属性 inputmode

  • "none" 无虚拟键盘。在应用程序或者站点需要实现自己的键盘输入控件时很有用。

  • "text" 使用用户本地区域设置的标准文本输入键盘。

  • "decimal"小数输入键盘,包含数字和分隔符(通常是“ . ”或者“ , ”),设备可能也可能不显示减号键。

  • "numeric" 数字输入键盘,所需要的就是 0 到 9 的数字,设备可能也可能不显示减号键。

  • "tel" 电话输入键盘,包含 0 到 9 的数字、星号(*)和井号(#)键。表单输入里面的电话输入通常应该使用 <input type="tel"> 。

如果没有设置这个属性,它的默认值是 "text",表明使用本地的标准文本输入键盘。

更多细节MDN

使用 inputmode 为 ["decimal"] 在苹果手机表现为:小数输入键盘,但是没有负号(-)

到底怎么让输入框只能输入数字(包括正数、负数和0)

使用 inputmode 为 ["numeric"] 在苹果手机表现为:数字输入键盘,但是没有小数点(.)和负号(-)

到底怎么让输入框只能输入数字(包括正数、负数和0)

这两个键盘都不是我想要的,然后我试了一下 type=number 发现是我想用的,如下:

到底怎么让输入框只能输入数字(包括正数、负数和0)

既有数字又有小数点(.)和负号(-)。

那如果使用 type=number 上面的校验逻辑还有效吗?发现无效了,那这不是完犊子了,不慌不慌,来看下具体原因:

当输入'-12.67' 数字的时候,校验是起作用的,但是输入 '-12.67-' 的时候就不起作用了,为什么呢?通过 log 输入框的 change 事件的 event.target.value,发现当输入 '-12.67-' 的时候 event.target.value 是 空字符串,也就是 input 发现输入的内容已经不是数字(包括正数、负数和0)的时候就会自动将 change 内容变为 空字符串。也就是 input 输入框自动过滤了非数字的情形,那校验可以简化一下:

const handleChange = (e: any)=>{ 
  const value = e.target.value; 
  const newValue = value 
  //.replace(/[^\d^\.^-]+/g, '') // 把不是数字,不是小数点的过滤掉   number 自动实现
  .replace(/^0+(\d)/, '$1') // 以0开头,0后面为数字,则过滤掉,取后面的数字 
  .replace(/^-0+(\d)/, '-$1') // 以-0开头,0后面为数字,则过滤掉,取后面的数字 
  //.replace(/-/g, (match: any, offset: number) => offset === 0 ? '-' : '') // 只允许第一个是负号-  对于 number 不起作用
  //.replace(/\./, '#').replace(/\./, '').replace(/#/, '\.')  // 只保留第一个小数点  number 自动实现

  // 只保留第一个小数点 //匹配负数,包括负整数和负小数 -xxx -xxx.xx 
  const matchNegative = newValue.match(/^-\d*(\.?\d{0,2})/g) 
  if (matchNegative) { 
    return onChange(matchNegative[0]) 
  } 
  //匹配正数,包括正整数和正小数 xx xx.xx 
  const matchPositive = newValue.match(/^\d*(\.?\d{0,2})/g) 
  if (matchPositive) { 
    return onChange(matchPositive[0]) 
  } 
  onChange(newValue) 
}

主要是注释了三行多余的代码,对于数字的过滤不需要了,因为 type=number 的 input 会自动完成;只允许第一个是负号- 和 只保留第一个小数点 对于 type=number 的 input 是不起作用的。也可以注释掉。

//.replace(/[^\d^\.^-]+/g, '') // 把不是数字,不是小数点的过滤掉   number 自动实现
//.replace(/-/g, (match: any, offset: number) => offset === 0 ? '-' : '') // 只允许第一个是负号-  对于 number 不起作用
//.replace(/\./, '#').replace(/\./, '').replace(/#/, '\.')  // 只保留第一个小数点  number 自动实现

这样,即使用户输入了‘-12.6-7’ 或者 ‘12.6.7’ 这种非数字的内容,通过 onBlur 事件会将 input 的 value 重置为 '0.00' 来告诉用户刚才输入的不是数字,我已经帮你转为数字了。

至此,满足要求的数字输入框完成了,如果大家发现不能满足我写的要求,欢迎留言吐槽,作者在线修改优化。

转载自:https://juejin.cn/post/7374343669207908361
评论
请登录