經驗總結:應對中文輸入法的字符串截斷方案(帶代碼示例)


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

所以呢,探索還將繼續:是否有兼容所有主流瀏覽器的方案,求路過的兄弟們支招。

 

本文代碼示例參見附件


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM