经验总结:应对中文输入法的字符串截断方案(带代码示例)

遇到这么个需求,允许用户修改自己的名片,名片最大长度支持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”事件都是内部做了一堆兼容性处理的。假如这个需求是要兼容所有主流浏览器的话就真跪了,虽然这个迟早有一天会变成残酷的现实。

所以呢,探索还将继续:是否有兼容所有主流浏览器的方案,求路过的兄弟们支招。

 

本文代码示例参见附件

posted @ 2014-03-14 00:38  程序猿小卡  阅读(8061)  评论(3编辑  收藏  举报