遇到這么個需求,允許用戶修改自己的名片,名片最大長度支持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”事件都是內部做了一堆兼容性處理的。假如這個需求是要兼容所有主流瀏覽器的話就真跪了,雖然這個遲早有一天會變成殘酷的現實。
所以呢,探索還將繼續:是否有兼容所有主流瀏覽器的方案,求路過的兄弟們支招。