textarea,input输入字符数限制。
在做开发时,有一种需求是对输入框(input,textarea)的字数做限制。如果按照JS的规定,字符串里所有的字符,长度都是1。但是有时候我们需要实现文本框中输入中文长度是2(或是3),其他非中文输入是1。这个时候就需要自己写一段代码来判断:
function getStringLengthForChinese (val) {
let str = val.toString()
let bytesCount = 0
for (let i = 0, len = str.length; i < len; i++) {
let c = str.charCodeAt(i)
if ((c > 0x0001 && c <= 0x007e) || (c >= 0xff60 && c <= 0xff9f)) { //这里是16进制表示,也可以用十进制
bytesCount = bytesCount + 1
} else {
bytesCount = bytesCount + 2
}
}
return bytesCount
}
这样就实现了获取一段字符串的长度,中文为2。
接下来就是对输入的字符串长度进行限制:
在input和textarea中,输入的长度限制需要添加maxlength属性。通常情况下,只要给maxlength一个固定的值就能解决问题。但是这里由于我们的中文预设长度是2。所以maxlegth的值应该是动态设置的。
我用vue来写,只要在模板中给maxlength绑定一个值:
<textarea :maxlength="maxlegth"
@input="textareaChange($event)"
></textarea>
<span>还可以输入{{codeNum}}个字符</span>
maxlegth需要给定一个预设值。
下面是限制字符输入数量的方法:
function computedLen(str, totalLength, maxLength) {
let rep = /[0-9a-zA-Z|\s]/ // 正则判断字母数字
let strArr = str.split('')
let totalLen = totalLength// 总的输入长度
let maxLen = maxLength// input或textarea上maxlength的值,这里由于中文算2个占位,所以传入的maxLength应该为totalLength的一半
let len = 0 // 已经输入的字符数
let leftLen = 0 // 剩余可输入字符数
strArr.forEach((val,key) => {
if (rep.test(val)) {
leftLen = Math.ceil(totalLen - len * 2)// 剩余输入数等于总长 - 输入数,乘以2是因为非中文的len只算0.5
if (leftLen === 0) {
return false // 如果剩余数是0,就退出循环,不能输入了
}
len = len + 0.5// 如果输入非中文,算加半个字符,maxlengtrh也加0.5,这样就实现了两个非中文长度等于一个中文
maxLen = maxLen + 0.5
} else {
len = len + 1
}
})
return {
maxLen: Math.ceil(maxLen) // 返回我们需要的maxlegt的值
}
}
function textareaChange (e) {
setTimeout(() => {
let count = this.computedLen(e.target.value, 30, 15)
this.maxLength = count.maxLen
}, 200)
}
上面代码的关键在于获取maxlength的值。这个值是动态的,在只输入中文的情况下,这个值等于我们的预设值,如果输入两个非中文,maxlength就会动态的加1。
vue watch监听剩余字数,并截断多出的字符。因为中文输入法的在非正式输入时,对于我们这个算法,会出现剩余字符数为负值的情况。所以需要增加以下代码,在输入数量超过时截断。
watch: {
'title' () {
this.codeNum = 30 - this.getStringLengthForChinese(this.title)
if (getStringLengthForChinese(this.title) > 30) {//如果占位数大于30
let arr = this.title.split('')// 输入字符串转为数组,依次推出最后一位元素
for (let i = arr.length - 1; i >= 0; i--) {
arr = arr.slice(0, i)// 每推出一个,将数组转为字符串,做一次占位数判断
this.title = arr.join('')
if (getStringLengthForChinese(this.title) <= 30) {
break
}
}
}
}
}
最终效果:
完整demo(vue写的):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<div id="app">
<textarea :maxlength="maxLength" v-model="title" @input="textareaChange($event)"></textarea>
<span>还能输入{{codeNum}}</span>
</div>
<body>
</body>
<script>
new Vue({
el: '#app',
data() {
return {
title: '',
maxLength: 15,
codeNum: 30
}
},
methods: {
getStringLengthForChinese(val) {
let str = val.toString()
let bytesCount = 0
for (let i = 0, len = str.length; i < len; i++) {
let c = str.charCodeAt(i)
if ((c > 0x0001 && c <= 0x007e) || (c >= 0xff60 && c <= 0xff9f)) { //这里是16进制表示,也可以用十进制
bytesCount = bytesCount + 1
} else {
bytesCount = bytesCount + 2
}
}
return bytesCount
},
textareaChange(e) {
setTimeout(() => {
let count = this.computedLen(e.target.value, 30, 15)
this.maxLength = count.maxLen
}, 200)
},
computedLen(str, totalLength, maxLength) {
let rep = /[0-9a-zA-Z|\s]/ // 正则判断字母数字
let strArr = str.split('')
let totalLen = totalLength // 总的输入长度
let maxLen = maxLength // input或textarea上maxlength的值,这里由于中文算2个占位,所以传入的maxLength应该为totalLength的一半
let len = 0 // 已经输入的字符数
let leftLen = 0 // 剩余可输入字符数
strArr.forEach((val, key) => {
if (rep.test(val)) {
leftLen = Math.ceil(totalLen - len * 2) // 剩余输入数等于总长 - 输入数,乘以2是因为非中文的len只算0.5
if (leftLen === 0) {
return false // 如果剩余数是0,就退出循环,不能输入了
}
len = len + 0.5 // 如果输入非中文,算加半个字符,maxlengtrh也加0.5,这样就实现了两个非中文长度等于一个中文
maxLen = maxLen + 0.5
} else {
len = len + 1
}
})
return {
maxLen: Math.ceil(maxLen) // 返回我们需要的maxlegt的值
}
}
},
watch: {
'title' () {
this.codeNum = 30 - this.getStringLengthForChinese(this.title)
if (getStringLengthForChinese(this.title) > 30) { //如果占位数大于30
let arr = this.title.split('') // 输入字符串转为数组,依次推出最后一位元素
for (let i = arr.length - 1; i >= 0; i--) {
arr = arr.slice(0, i) // 每推出一个,将数组转为字符串,做一次占位数判断
this.title = arr.join('')
if (getStringLengthForChinese(this.title) <= 30) {
break
}
}
}
}
}
})
</script>
</html>