最近遇到一个比较常用的需求,需要限制文本框输入数字 本来开始是使用oninput事件来做限制 后来测试发现会有一些不可控状况 比如在限制只能输入数字的输入框用输入法打字之后虽然输入框中的限制是正确的 但此时v-model确失效了 无法正确的捕获到input事件 造成不管输入框输入什么都没有正确value的情况 多方搜索无果 只能自己实现一个指令来解决问题 并增加了一个自定义的修饰符来应对浮点数的场景
function onInput(el, ele, binding, vnode) {
// 这里是用闭包来实现保留第一个小数点
function regHandle() {
let flag = 0
return () => (flag++ === 0) ? '.' : ''
}
function handle() {
// 只保留数字
let val = ele.value
// modifiers为修饰符对象,传入了float,则其float属性为true
if (binding.modifiers.float) {
// 保留下我们想要的部分 数字和小数点
val = val.replace(/[^\d.]/g, '')
// 不允许输入多个. 只保留第一个.
val = val.replace(/\./g, regHandle())
// 不允许以小数点开头
val = val.replace(/^\./g, '0.')
if (binding.value) {
// 期望保留的最大小数位数 只允许正整数
let points = +binding.value
if (!isNaN(points)) {
if (!Number.isInteger(points) ||
points < 0) {
points = 0
}
const str = ''
const reg = new RegExp(`^(\\d+)\\.(\\d\{${points}}).*$`)
if (points === 0) {
// 不需要小数点
val = val.replace(reg, '$1')
} else {
// 通过正则保留小数点后指定的位数
val = val.replace(reg, '$1.$2')
}
}
}
} else {
val = ele.value.replace(/[^\d]/g, '')
}
ele.value = val
// 这里是关键的部分 需要我们手动的去触发input事件来保证v-model的响应性
if (vnode.componentInstance) {
vnode.componentInstance.$emit('input', ele.value)
} else {
vnode.elm.dispatchEvent(new CustomEvent('input', ele.value))
}
}
return handle
}
const numberLimit = {
bind(el, binding, vnode) {
const ele = el.tagName === 'INPUT' ? el : el.querySelector('input')
ele.addEventListener('input', onInput(el, ele, binding, vnode), false)
}
}
此时我们就实现了一个数字限制的自定义指令 大家也可以自己扩展更多的规则 例如最大值最小值正数负数等
