本文转自:http://www.cnblogs.com/chyingp/p/3599641.html
遇到这么个需求,允许用户修改自己的名片,名片最大长度支持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”事件都是内部做了一堆兼容性处理的。假如这个需求是要兼容所有主流浏览器的话就真跪了,虽然这个迟早有一天会变成残酷的现实。
所以呢,探索还将继续:是否有兼容所有主流浏览器的方案,求路过的兄弟们支招。