制做一个文本框限制最大字数,实时监听当前已经输入的字数,并显示出来。期初我实现这个功能的方法很简单:给textarea控件添加onkeyup事件方法,在方法中将textarea值的长度打印出来,并给textarea添加一个maxlength属性设置长度限制便可。代码以下:javascript
<div class="form-group needCount"> <textarea id="txt0" maxlength="10"></textarea> <p><span id="txtNum0">0</span>/10</p> </div>
var txt0 = document.getElementById("txt0"); var txtNum0 = document.getElementById("txtNum0"); txt0.addEventListener("keyup",function(){ txtNum0.textContent = txt0.value.length; });
这样貌似很简单就实现了,在英文输入法下一切还都ok,但当咱们用输入法输入中文时,问题很快就来了,好比咱们要输入“文章”一词就要输入“wenzhang”浏览器会监听到8词keyup事件。在一些浏览器(如safari)中,若是这个过程超过maxlength甚至会阻止你继续输入。所以单纯的监听keyup事件显示是不够的。html
每次按下键盘就会触发监听事件java
通过查阅前辈们的解决方案,发现了两个以前没有据说过的属性“compositionstart”和“compositionend”。
MDN上的解释:compositionstart 事件触发于一段文字的输入以前(相似于 keydown 事件,可是该事件仅在若干可见字符的输入以前,而这些可见字符的输入可能须要一连串的键盘操做、语音识别或者点击输入法的备选词)。
compositionend就是对应的就是一段文字输入的事件。ios
有了这两个事件,咱们就能够作一个“开关”,一旦检测到开始使用输入法输入一段文字了,就把这个“开关打开”,检测到一段文字输入完毕了,就关闭这个“开关”。接下来咱们在以前的keyup方法中添加一个断定条件,若是开关关闭,正常打印出textaarea值的长度;若是开关打开,中止打印。而输入一段文字时,监听输入完成的事件“compositionend”,将textarea的值的长度打印出来。这样不管是否开启了输入法都能正确的打印出控件值的长度了。数组
代码以下:(其中变量chnIpt就是表明是否开启了输入法进行输入的关键变量“开关”)浏览器
var txt0 = document.getElementById("txt0"); var txtNum0 = document.getElementById("txtNum0"); var chnIpt0 = false; txt0.addEventListener("keyup",function(){ if(chnIpt0 ==false){ countTxt(); } }); txt0.addEventListener("compositionstart",function(){ chnIpt0 = true; }) txt0.addEventListener("compositionend",function(){ chnIpt0 = false; countTxt(); }) function countTxt(){ if(chnIpt0 == false){ txtNum0.textContent = txt0.value.length; } }
如此实现的效果就是英文输入法下没放开键盘就会进行一次字数统计,输入法输入中文时,输入结束时才会统计字数。优化
固然一个完整的插件必定是能够复用的。若是页面里须要多个文本框都要限制字数如何实现。
咱们须要考虑如下几个问题:spa
要解决问题1,首先想到建立一个数组,数组中的每个元素经过不一样的Id获取一个元素。一个独立的过程当中咱们须要获取两个元素:txt和txtNun,一个关键变量chnIpt,所以咱们要建立三个数组。为了方便理解,假定页面中有须要三组控件:插件
<div class="form-group needCount"> <textarea id="txt0" maxlength="10" onfocus="ff(0)"></textarea> <p><span id="txtNum0">0</span>/10</p> </div> <div class="form-group needCount"> <textarea id="txt1" maxlength="10" onfocus="ff(1)"></textarea> <p><span id="txtNum1">0</span>/10</p> </div> <div class="form-group needCount"> <textarea id="txt2" maxlength="10" onfocus="ff(2)"></textarea> <p><span id="txtNum2">0</span>/10</p> </div>
则建立数组的过程:code
var txt0 = document.getElementById("txt0"); var txt1 = document.getElementById("txt1"); var txt2 = document.getElementById("txt2"); var txtNum0 = document.getElementById("txtNum0"); var txtNum1 = document.getElementById("txtNum1"); var txtNum2 = document.getElementById("txtNum2"); var chnIpt0 = false; var chnIpt1 = false; var chnIpt2 = false; var txt=[txt0,txt1,txt2]; var txtNum=[txtNum0,txtNum1,txtNum2]; var chnIpt=[chnIpt0,chnIpt1,chnIpt2];
这样txt就是textarea控件的数组,txtNum就是现实字数的标签的数组,chnIpt就是判断“开关”的关键变量数组,以待调用。
如今思考第二个问题“同是监听“keyup”和“compositionend”如何区分不一样的textarea”。或者说,咱们怎么判断当前输入的textarea是txt元素中的第几个呢。
这里就须要表单控件都具备的focus事件进行区别,在focus事件的方法中传入表明数组索引的参数,从而选择调用数组中相应那个元素。
代码以下:(ff(i)即为focus事件调用的方法参数为索引值)
function ff(i){ txt[i].addEventListener("keyup",function(){ if(chnIpt[i] ==false){ txtNum[i].textContent = txt[i].value.length; } }); txt[i].addEventListener("compositionstart",function(){ chnIpt[i] = true; }); txt[i].addEventListener("compositionend",function(){ chnIpt[i] = false; txtNum[i].textContent = txt[i].value.length; }); }
咱们再来考虑最后一个问题。目前是已知页面中须要几组文本框的状况,咱们能够手动建立,费时费力代码也不美观。
进一步优化一下建立数组的过程:给每个独立的组件一个class,获取具备这个class的元素的长度,循环这个class的长度,给数组中添加元素便可。如此处理,引用一段脚本没必要再作更改,只须要在html中添加相应的组件便可。
JS代码以下
var txt = [],txtNum = [],chnIpt = []; var needCount = document.getElementsByClassName("needCount"); for(var i=0;i<needCount.length;i++){ txt[i] = document.getElementById("txt"+i); txtNum[i] = document.getElementById("txtNum"+i); chnIpt[i] = false; }
最终完整的JS代码:
var txt = [],txtNum = [],chnIpt = []; var needCount = document.getElementsByClassName("needCount"); for(var i=0;i<needCount.length;i++){ txt[i] = document.getElementById("txt"+i); txtNum[i] = document.getElementById("txtNum"+i); chnIpt[i] = false; } function ff(i){ txt[i].addEventListener("keyup",function(){ if(chnIpt[i] ==false){ txtNum[i].textContent = txt[i].value.length; } }); txt[i].addEventListener("compositionstart",function(){ chnIpt[i] = true; }); txt[i].addEventListener("compositionend",function(){ chnIpt[i] = false; txtNum[i].textContent = txt[i].value.length; }); }
一组字符输入彻底以前不会监听自述变化
demo连接:点我
如此实现知识该组件我尝试以来最为接近预期的实现方法,在pc端主流浏览器(ie9以上),安卓、ios的原生键盘输入、“讯飞语音”的语音输入法效果良好。但在一些特定状况下仍然会出现问题,譬如:
以上两个问题至今没有找到解决,以及,我发如今火狐浏览器下会执行两次“compositionend”,但并不影响字数统计。诸如这些疑问还烦请网上的各位高人如遇到相似问题给予在下指正与指点。
最后我想说一句话:“若是事件再也不有浏览器之间的差别,世界将变成美好的人间!”