vue设置输入框输入长度_基于vue的限制输入框可输入字节数的解决方案
2018年07月11日
基于vue的限制输入框可输入字节数的解决方案
需求:input输入框输入上限4个字节、达到上限则不能继续输入,其中1个英文表示1个字节、1个中文表示2个字节。
看到这个需求,第一个想到的就是input元素的maxlength属性。
MDN对input的maxlength属性的说明是:
如果 type 的值是 text, email, search, password, tel, 或 url,那么这个属性指明了用户最多可以输入的字符个数(按照Unicode编码方式计数);对于其他类型的输入框,该属性被忽略。它可以大于 size 属性的值。如果不指定这个属性,那么用户可以输入任意多的字符。如果指定为一个负值,那么元素表现出默认行为,即用户可以输入任意多的字符。本属性的约束规则,仅在元素的 value 属性发生变化时才会执行。译者注:ie10+
从中我们可以得知两个有用信息:
maxlength可以用来限制输入长度;
maxlength是按字符数进行限制的,不能区分中英文。
根据这两个信息,我们得到了实现此需求的基本方案:
在用户输入之后计算输入内容的字节数和剩余可输入字节数,并动态地改变input元素的maxlength属性值。
计算输入内容的字节数
将输入内容的每一个中文字符替换成两个英文字符,计算其字符长度:
// 返回字符串str的长度,其中中文占2个长度单位,英文等字符占1个长度单位
let length = (str) => {
var r = /[^\x00-\xff]/g
return str.replace(r, 'mm').length
}
动态地改变maxlength值
思考解决方法过程中,分别实现过 通过@input事件触发方法来修改maxlength、把处理方法放到mixin里、封装一个处理方法的指令 3种方式,考虑可移植性及修改作用域字段的可行性,最终采用指令方式。
下面对主要逻辑进行说明:
maxlength:input元素maxlength属性值
limit:限制字节数
中文多消耗的字节数中文字符个数maxlength = limit - 中文多消耗的字节数 = limit - 中文字符个数
当连续输入的内容的字节数超过limit时,需要对内容进行截断:
截断长度 = 一个一个输入的非中文字符串长度 + 允许输入的中文字符串长度 = 非中文字符长度 + Math.floor((limit - 非中文字符所占字节数) / 2)
截断后内容 = 截断前内容.substring(0, 截断长度)
当在两个非中文之间(非末尾)输入超过限制的中文时,截取长度会多1个字节,需要再次校准输入内容:
校准后内容 = 校准前内容.substring(0, 截断长度 - 1)
vue自定义指令的钩子update是在VNode更新时调用,所以我们的处理方法在update钩子中进行;
vue自定义指令钩子函数会被传入参数el、binding、vnode、oldVnode。我们可以通过el拿到指定绑定的DOM;通过binding拿到指令的绑定值和传入的参数等等;通过vnode可以拿到vue编译生成的虚拟节点,其中vnode.context是虚拟节点上下文、也就是this,通过它我们可以改变指令所在组件data的任一变量。
其他方案及其缺陷
input v-model 使用 computed
export default {
data () {
return {
contentStore: ''
}
},
computed: {
content: {
get () {
return this.contentStore
},
set (val) {
this.contentStore = maxlength(val, 4) // maxlength是截取字符串方法
}
}
}
}
问题:虽然变量长度被限制了,但输入框仍可继续输入。
打印input元素的value,得到:
双向数据绑定失效了。
从v-model双向数据绑定原理入手
方法1中双向数据绑定失效了,那么我们来研究下input双向数据绑定的实现原理,看看能否找到突破口。
input的v-model只是一个简化的指令,它的双向数据绑定原理如下:
msg = e.target.value">
msg = e.target.value">
改写双向数据绑定方法:
methods: {
maxlengthInput (e) {
this.contentStore = maxlength(e.target.value, 4) // maxlength是截取字符串方法
}
}
结果和方法1一样,输入框仍可继续输入。
这里input的value是根据contentStore进行改变的,那直接修改DOM是否可行呢?
直接修改DOM的value值
在方法2的基础上,修改maxlengthInput方法,通过DOM操作来直接修改input的value,并打印出input的value进行观察:
methods: {
maxlengthInput (e) {
let value = maxlength(e.target.value, 4)
this.contentStore = value
document.querySelector('.el-input__inner').value = value
console.log(document.querySelector('.el-input__inner').value)
}
}
根据打印出来的内容可以看到,实际上input的value被改变了,而输入框内仍可正常输入,原因可能是element-ui的处理顺序问题(用原生input元素没有这个问题),那我们把dom赋值操作延后到下一个tick:
maxlengthInput (e) {
let value = maxlength(e.target.value, 4)
this.contentStore = value
setTimeout(() => {
document.querySelector('.el-input__inner').value = value
console.log(document.querySelector('.el-input__inner').value)
}, 0)
}
结果是可行的:
所以结束了吗?来输入中文试试:
中文输入法的每个拼音字母都被认为是一个有效的输入,这个缺陷是致命的。
compositionend事件
输入中文时,会先后触发compositionstart(输入中文前)、compositionend(中文输入完成后)事件。所以可以利用compositionend事件解决方法3的问题。
但是,删除输入内容时是无法触发compositionend事件的,那么,就需要同时监听input事件。
input事件会比compositionend事件先触发,所以会出现方法3的无法输入中文的问题,不过只要把input事件处理延迟到compositionend之后(比如用setTimeout),这个问题也可以解决。随之而来的问题就是输入非中文字符时,input事件处理也会延迟,用户体验不太友好。
总结
对于区分中英文来限制输入框输入长度的需求,利用input元素的maxlength属性是比较便利和可行的方式;使用vue自定义指令可以提高方法的可用性(再把指令封装成插件,可以很大地提高方法的可移植性)。
当然,vue自定义指令方法有一个问题,当在非末尾输入超过limit的内容时,会截断末尾的字符、而不是截断正在输入的内容。解决这个问题需要比较输入前和输入后的字符串、再把diff出的字符串截断掉。虽然这个问题可以解决,但性价比不高,对于当前的需求也没有解决的必要。
参考
更多推荐
所有评论(0)