需求
最近有个需求,在input输入之后过滤不是数字的字符,这种需求是很常见的,于是就简单写了一个自定义指令,代码如下:
<el-input v-model="form.hsCode" v-checkInput.numeric />
import type { App, Directive, DirectiveBinding } from 'vue'
const checkInput: Directive = (el: HTMLElement, binding: DirectiveBinding) => {
let inp: HTMLInputElement
if (el.tagName.toLowerCase() === 'input') {
inp = el as HTMLInputElement
}
else {
inp = (el.querySelector('input') || el.querySelector('textarea')) as HTMLInputElement
}
function numeric(value: string) {
return value.replace(/\D/g, '')
}
inp.addEventListener('input', (e) => {
if (binding.modifiers.numeric)
return (inp.value = numeric(inp.value.toString()))
})
}
export default (app: App) => app.directive('checkInput', checkInput)
以上代码自测的时候并没有发现有什么问题,产品测试时在中文输入法下疯狂输入拼音后发现问题,输入结束后el-form的校验始终获取不到输入的值,查询后才知道当我们输入中文后,由于拼音也会触发input事件,以至于输入框一直在触发input事件,造成了卡顿,无法获取到值。
解决方案
方案一:使用
compositionstart和compositionend解决这个问题
- compositionstart:开始新的输入合成时会触发
compositionstart事件 - compositionend:当文本段落的组成完成或者取消时会触发
componsitionend事件 实现代码如下
import type { App, Directive, DirectiveBinding } from 'vue'
const checkInput: Directive = (el: HTMLElement, binding: DirectiveBinding, vnode: any) => {
let inp: HTMLInputElement
if (el.tagName.toLowerCase() === 'input') {
inp = el as HTMLInputElement
}
else {
inp = (el.querySelector('input') || el.querySelector('textarea')) as HTMLInputElement
}
function numeric(value: string) {
return value.replace(/\D/g, '')
}
inp.addEventListener('compositionstart', () => {
vnode.inputLocking = true
})
inp.addEventListener('compositionend', () => {
if (!vnode.inputLocking)
return
vnode.inputLocking = false
inp.dispatchEvent(new Event('input'))
})
inp.addEventListener('input', (e) => {
e.preventDefault()
if (vnode.inputLocking) {
return
}
if (binding.modifiers.numeric)
return (inp.value = numeric(inp.value.toString()))
})
}
export default (app: App) => app.directive('checkInput', checkInput)
方案二:不使用自定义指定,直接在使用input事件,将处理后的值赋值给form对象相应的属性
<el-input
v-model="formData.hsCode"
@input="form.hsCode = form.hsCode.replace(/\D/g, '')"
/>