在作开发时,有一种需求是对输入框(input,textarea)的字数作限制。若是按照JS的规定,字符串里全部的字符,长度都是1。可是有时候咱们须要实现文本框中输入中文长度是2(或是3),其余非中文输入是1。这个时候就须要本身写一段代码来判断:html
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。vue
接下来就是对输入的字符串长度进行限制:算法
在input和textarea中,输入的长度限制须要添加maxlength属性。一般状况下,只要给maxlength一个固定的值就能解决问题。可是这里因为咱们的中文预设长度是2。因此maxlegth的值应该是动态设置的。
我用vue来写,只要在模板中给maxlength绑定一个值:npm
<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。app
vue watch监听剩余字数,并截断多出的字符。由于中文输入法的在非正式输入时,对于咱们这个算法,会出现剩余字符数为负值的状况。因此须要增长如下代码,在输入数量超过期截断。ui
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 } } } } }
最终效果:this
完整demo(vue写的):spa
<!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>