遇到這么個需求,允許用戶修改自己的名片,名片最大長度支持8個漢字(24個字節),當用戶輸入超過8個字節,則不允許用戶繼續輸入。
最初的思路:oninput你好
很常見的需求,覺得駕輕就熟,監聽input事件,當輸入內容發生變化的時候,獲得用戶輸入內容,並進行截斷操作(如果超出的話)。主要代碼如下。一切顯得那么美好,直到中文輸入法出現。
ps:本文用例均在 chrome 版本 33.0.1750.146下測試
$('#text').on('input', function() {
var value = $(this).val();
if(Str.byteLen(value, 3)>24){
$(this).val(Str.getMaxlen(value, 24));
}
});
完整代碼如下,有興趣可以看下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>demo</title>
</head>
<body>
<input id="text" placeHolder="最大支持24個字節" />
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
var Str = {
byteLen : function (str, len){
//正則取到中文的個數,然后len*count+原來的長度。不用replace
var factor = len || 2;
str += '';
var tmp = str.match(/[^\x00-\xff]/g) || [];
var count = tmp.length;
return str.length + (factor-1)*count;
},
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);
//sResult+=sChar;
L+= sChar.match(/[^\x00-\xff]/) !== null ? 3 : 1;
if(L > maxlen){
stop = true;
}else{
sResult+=sChar;
i++;
}
}
return sResult;
}
};
$('#text').on('input', function() {
var value = $(this).val();
if(Str.byteLen(value, 3)>24){
$(this).val(Str.getMaxlen(value, 24));
}
});
</script>
</body>
</html>
oninput的局限:中文輸入法帶來的難題
上面的解決方案對於普通的文本輸入,如英文字母、數字等的輸入是ok的。但當用戶通過中文輸入法(比如QQ拼音)時,就會遇到一些問題,我們簡單改下上面的代碼,看看究竟會有什么問題。就加多了個log打印。
$('#text').on('input', function() {
var value = $(this).val();
console.log('當前輸入:'+value); // 打印當前輸入的值
if(Str.byteLen(value, 3)>24){
$(this).val(Str.getMaxlen(value, 24));
}
});
下面是輸入過程中的截圖。可以看到,用戶使用中文輸入法輸入的過程中,“input”事件被不斷地觸發着,這會帶來什么問題呢?相信你已經想到了——會導致程序對當前用戶輸入字符實際長度的誤判。比如用戶輸入“程序猿”三個漢子,實際占用9個字節,但對上面的程序來說,取到的字節數為"chengxuyuan".length == 11。在用戶輸入達到邊界值時,就會莫名其妙地將用戶的輸入截斷,導致中文輸入無法接續(感興趣的同學可以自己試下)

解決思路一:compositionstart、compositionend
在萬能的幼稚園群里拋出問題后,有個兄弟提出了個方案:可以采用compositionstart、compositionend來捕獲IME(input method editor)的啟動和關閉事件。說實話,這兩事件聽都沒聽過,但既然有這么個解決方案,暫且試一下,再次修改代碼
$('#text').on('input', function() {
if($(this).prop('comStart')) return; // 中文輸入過程中不截斷
var value = $(this).val();
console.log('當前輸入:'+value);
if(Str.byteLen(value, 3)>24){
$(this).val(Str.getMaxlen(value, 24));
}
}).on('compositionstart', function(){
$(this).prop('comStart', true);
console.log('中文輸入:開始');
}).on('compositionend', function(){
$(this).prop('comStart', false);
console.log('中文輸入:結束');
});
輸入過程截圖如下,可以看到,當compositionstart事件觸發,就停止對輸入字符的截斷操作,而是耐心等待用戶輸入的結束

按下空格鍵,中文輸入結束,此時再去進行字符長度的判讀和截斷

未完的探索
正如正文最前面強調的,本文的用例都是在chrome特定版本下進行測試,顯然compositionstart、compositionend並不是一個兼容所有瀏覽器的方案。包括jQuery的“input”事件都是內部做了一堆兼容性處理的。假如這個需求是要兼容所有主流瀏覽器的話就真跪了,雖然這個遲早有一天會變成殘酷的現實。
所以呢,探索還將繼續:是否有兼容所有主流瀏覽器的方案,求路過的兄弟們支招。
