到底怎么让输入框只能输入数字(包括正数、负数和0)
最近在做一个移动端输入框,表示成本,用户只能输入数字(包括正数、负数和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]
*
: 匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 \*
+
:匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 \+
?
: 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ?
字符,请使用 \?
修饰符:
g
: global
- 全局匹配 | 查找所有的匹配项。
就用到这么多的正则语法,是不是比较简单,
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"
] 在苹果手机表现为:小数输入键盘,但是没有负号(-)
使用 inputmode
为 ["numeric"
] 在苹果手机表现为:数字输入键盘,但是没有小数点(.)和负号(-)
这两个键盘都不是我想要的,然后我试了一下 type=number
发现是我想用的,如下:
既有数字又有小数点(.)和负号(-)。
那如果使用 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