input限制字符长度 - composition

input 监听

关于js监听input输入的方法很容易想到 oninputonchange (包括ie下onpropertychange)这些都是监听用户敲下键盘改变input内容的方法,正常情况下没有问题或者说输入英文没有问题;
但是!

天朝输入法是那么的棒(当然其他国家不在考虑范围内);说到中文输入法这里要引入另一个概念;

即:

  • 直接输入
  • 非直接输入

直接输入

什么是直接输入呢?
就是用户敲下键盘后直接将结果填充到input输入框中的,也就是输入英文字母,字节以及数字这种不需要选择的内容(或者把输入法改为英文状态);

这个时候我们基本上不用考虑特殊情况了,监听事件直接使用 input 或者 change 都可以,足够满足使用;

非直接输入

然后我们来说说这个非直接输入,经过上面的解释那么非直接输入也就比较好理解了;
就是用户选择了中文输入然后在用户在input中输入中文前,input并不是空的而是把用户敲的键盘对应按键暂时放在了input中,直到用户选择了最终的结果后替换刚才存放在input里面的内容;这时问题来了,刚才存放在input里面的字节是否会触发input或者change事件呢?

我们试下:

E2BCB28D-B0A2-484C-B40B-AA00565E4E89.png

可以发现其实即使没有选择最终的结果但是依然触发了input的input事件,但是有些时候我们不希望是这样的,比如这样一个业务需求:

  • 给input设置一个输入限制,最多输入12个字节(也就是6个汉字)

这个需求应该属于比较常见的,那么正常情况下我们可以给input设置 maxlength 属性来限制最大长度,

但是!

maxlength是不区分中英文的,也就是说不管你是中文还是英文我都算一个,即 ‘w’.length === ‘我’.length 是成立的,所以我们需要用正则来验证中英文来限制用户输入……

通常我是这样做的当用户输入的内容达到指定长度之后在输入的时候我就截取前面有效的内容显示,也就是说除非用户删除了一些内容,否则不允许在输入新内容了;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>demo</title>
</head>
<body>
<input id="text" placeHolder="最大支持12个字节" maxlength="12" />
<script type="text/javascript">
var Str = {
byteLen : function (str){
//正则取到中文的个数,然后len*count+原来的长度。不用replace
str += '';
var tmp = str.match(/[^\x00-\xff]/g) || [];
return str.length + tmp.length;
},
getMaxlen : function(str,maxlen){
var sResult = '', L=0, i=0, stop = false, sChar;
if(str.replace(/[^\x00-\xff]/g,'xxx').length <= maxlen){
return str;
}
while(!stop){
sChar = str.charAt(i);
L+= sChar.match(/[^\x00-\xff]/) !== null ? 2 : 1;
if(L > maxlen){
stop = true;
}else{
sResult+=sChar;
i++;
}
}
return sResult;
}
};
document.querySelector('#text').addEventListener('input', function(){
var value = this.value,
maxlength = this.getAttribute('maxlength');
if(Str.byteLen(value)>maxlength){
this.value = Str.getMaxlen(value, maxlength);
}
})
</script>
</body>
</html>

接着问题来了 - -!

当用户输入中文的时候发现我居然无法连续输入6个汉字!?

呐尼?

比如:用户需要输入“长长的筷子”,结果不能输入中文!对应的拼音(不考虑五笔或其他方式)是“changchangdekuaizi“ - 很明显这已经超过12个字节了,但是用input或者change监听的话,已经触发了超过12字节长度的条件了,那么我就不会让它输入了,也就是阻止了用户的输入行为(实际测试某些浏览器+某些输入法也可以完成输入,此处考虑特殊环境不能输入的情况);
这很反人类,这样不好,这样容易没有朋友!

改!
然后加了一顿的判断,哈哈~

实现了类似的需求/表示不想在写这个了~

super hero

噔~ 噔~ 噔~ 噔!

input composition 家族登场

这个 composition 家族 有

  • compositionstart
  • compositionupdate
  • compositionend

[具体解释请查看文档说明]

三个方法

我们可以只看中compositionstartcompositionend

利用这两个方法可以实现一个不截断用户输入但能限制用户输入长度的效果

1
2
3
4
5
6
7
8
9
10
11
var inputLock = false;
document.querySelector('#text').addEventListener('compositionstart', function(){
inputLock = true;
})
document.querySelector('#text').addEventListener('compositionend', function(){
inputLock = false;
})
document.querySelector('#text').addEventListener('input', function(){
if(!inputLock)
console.log(this.value);
});

实际测试compositionend的触发条件必须要选定内容也就是非直接输入内容才会触发并且是在input事件之后才会触发;所以我们不能直接用来检测用户的输入但是可以用这个搞!事!情!

7563E5E0-B443-4B38-8260-134E66BF5755.png

解决

使用compositionstartcompositionend 结合前面的字数限制可以实现下面的效果!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>demo</title>
</head>
<body>
<input id="text" placeHolder="最大支持12个字节" maxlength="12" />
<script type="text/javascript">
var Str = {
byteLen : function (str){
//正则取到中文的个数,然后len*count+原来的长度。不用replace
str += '';
var tmp = str.match(/[^\x00-\xff]/g) || [];
return str.length + tmp.length;
},
getMaxlen : function(str,maxlen){
var sResult = '', L=0, i=0, stop = false, sChar;
if(str.replace(/[^\x00-\xff]/g,'xxx').length <= maxlen){
return str;
}
while(!stop){
sChar = str.charAt(i);
L+= sChar.match(/[^\x00-\xff]/) ? 2 : 1;
if(L > maxlen){
stop = true;
}else{
sResult+=sChar;
i++;
}
}
return sResult;
}
};
var inputLock = false;
document.querySelector('#text').addEventListener('compositionstart', function(){
inputLock = true;
})
document.querySelector('#text').addEventListener('compositionend', function(){
inputLock = false;
var value = this.value,
maxlength = this.getAttribute('maxlength');
if(Str.byteLen(value) > maxlength){
this.value = Str.getMaxlen(value, maxlength);
}
})
document.querySelector('#text').addEventListener('input', function(){
if(!inputLock){
var value = this.value,
maxlength = this.getAttribute('maxlength');
if(Str.byteLen(value) > maxlength){
this.value = Str.getMaxlen(value, maxlength);
}
}
});
</script>
</body>
</html>