关于javascript实时显示textarea剩余字符数

今天在百度的项目中碰到一个问题,就是实现javascript实时显示textarea剩余字符数的 功能,咋一看,这个功能也太简单了吧,一般都是只用keydown和keyup事件监听textarea的字符数就可以了,但是QA检测需要支持鼠标右键 的相关事件(包括粘贴,撤销,删除和剪切等),而且要求鼠标直接拖动文字至textarea中也要实时改变字数,由于这些方法没有直接按键盘,所以光 keydown和keyup事件监听是不够的。
此时就需要用onpropertychange事件了,该事件与onchange事件存在本质区别,onpropertychange事件是当控件里的内容一改变马上触发事件(注意:onpropertychange事件仅限于使用在普通的html上,使用过struts的html:textarea 标签的不包含该事件,否则会报错),但是onpropertychange是IE专享的,而且好像不能用attachEvent绑定,而要直接obj.onpropertychange = fun;其他浏览器需要用oninput事件替代。
一个简单的demo:

<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>无标题文档</title>
</head>
<body>
<textarea name="" id="textarea"></textarea>
<p id="remain"></p>
<script type="text/javascript">
var textareaObj=document.getElementById("textarea"),
    remainObj=document.getElementById("remain"),
    num=0;
if(/msie/i.test(navigator.userAgent)){
    textareaObj.onpropertychange=function(){
        num=1000-this.value.length;
        remainObj.innerHTML="剩余"+num+"字";
    }    
}
else{
    textareaObj.oninput=function(){
        num=1000-this.value.length;
        remainObj.innerHTML="剩余"+num+"字";
    }
}
</script>
</body>
</html>

上面这个demo支持FF,chrome、ie7和safari所有的事件,包括键盘的输入、删除和直接鼠 标拖入,也可以鼠标右键里面的粘贴、删除、撤销和剪切等,opera也支持这些事件,但是不支持鼠标直接拖入,ie8,ie9不支持鼠标的右键事件(除了 粘贴)。ie9支持键盘输入,不支持键盘删除。
onpropertychange事件没法直接鼠标右键里面的删除和撤销等事件,而且也没办法直接监听这些事件,不像鼠标右键的剪切粘贴和直接拖动等能够用onpaste、oncut和ondrop监听,所以这方面还没找到有效的办法,如果有童鞋知道,请告知,万分感激!
demo:

/*计算剩余字符,base.remainWord("#textarea",10,"#wordsNum");*/
    remainWord:function(textareaId, totalNum, remainId) {
        /*
        textareaId:代表textarea的ID;
        totalNum:代表可输入的总共的字符数;
        remainId:显示剩余字符数的ID;
        */
        $(textareaId).each(function() {
            var self = $(this), remainObj = $(remainId), num = 0;
            function fun() {
                num = totalNum - self.val().length;
                if (num >= 0) {
                    remainObj.css("color", "#828181").html("剩余" + num + "个字");
                    self.removeClass("error");
                } else {
                    remainObj.css("color", "#f00");
                    self.addClass("error").val(self.val().substring(0, totalNum));
                }
                if (self.val() == "") {
                    self.prev("label").html("请输入想要说的问题,们会及时给您反馈。");
                } else {
                    self.prev("label").html("");
                }
            }
            //ie用onpropertychange事件,IE9不支持鼠标右键里面的删除,onpropertychange不支持很多事件,需要单独写
            if (/msie/i.test(navigator.userAgent)) {
                self[0].onpropertychange = fun;
                self.keydown(fun);
                self.keyup(fun);
                base.bindEvent(self[0], "paste", function() {
                    setTimeout(fun, 100);
                });
                base.bindEvent(self[0], "cut", function() {
                    setTimeout(fun, 100);
                });
            } else {
                base.bindEvent(self[0], "input", fun);
            }
            //opera和IE拖动事件
            if (/opera/i.test(navigator.userAgent) || /msie/i.test(navigator.userAgent)) {
                //防止用户拖动内容到输入框
                base.bindEvent(self[0], "drop", function() {
                    setTimeout(fun, 100);
                });
                base.bindEvent(self[0], "dragend", function() {
                    setTimeout(fun, 100);
                });
            }
        });
    },

最终解决方式如下:

/*检测字数通用fun*/
    checkWord: function(obj, fun, speed) {
        //部分ie中的onpropertychange事件并不能检测鼠标右键的删除和撤销等事件,opera的oninput事件不能检测直接拖动内容到textarea事件drop&&dragend,故这利用定时器解决
        if (base.browser() == "ie6" || base.browser() == "ie7" || base.browser() == "ie8" || base.browser() == "ie9" || base.browser() == "opera") {
            var timer;
            $(obj).focus(function() {
                timer = setInterval(fun, speed);
            });
            $(obj).blur(function() {
                clearInterval(timer);
            });
        }
        //FF,Chrome,safari等浏览器可以利用oninput事件监听所有的事件,包括keydown,keyup,鼠标右键中的cut,paste和删除,撤销等所有事件,包括直接拖动drop等也支持
        else {
            base.bindEvent(obj, "input", fun);
        }
    },
    /*计算剩余字符,base.remainWord("#textarea","#wordsNum");*/
    remainWord: function(textareaId, remainId) {
        /*
		textareaId:代表textarea的ID;
		remainId:显示剩余字符数的ID;
		*/
        $(textareaId).each(function() {
            var self = $(this),
            	remainObj = $(remainId),
            	num = 0;
            function fun() {
                num = base.totalNum - self.val().length;
                if (num > 0) {
                    remainObj.css("color", "#828181").html("剩余" + num + "个");
                    self.removeClass("error");
                    if (num == base.totalNum) {
                        remainObj.html("最多" + base.totalNum + "字");
                    }
                } else {
                    remainObj.css("color", "#f00").html("剩余0个");
                    self.addClass("error");
                    //防止ie下输入全部内容后不能ctrl+a全选
                    if (num != 0) {
                        self.val(self.val().substring(0, base.totalNum));
                        //防止输入全部内容后能在前面输入字符,同时删除了后面的字符
                        self.unbind("keydown");
                        self.keydown(function(event) {
                            //排除一些删除按钮和方向按钮等,getTextareaSelectVal是为了当选择textarea一部分内容后,就可以输入内容,否则不能输入,self.val().length >= base.totalNum是为了删除textarea一部分内容后,能够重新输入
                            if (! (event.keyCode == 8 || event.keyCode == 9 || event.keyCode == 46 || event.keyCode == 37 || event.keyCode == 38 || event.keyCode == 39 || event.keyCode == 40 || event.ctrlKey && event.keyCode == 65 || getTextareaSelectVal()) && self.val().length >= base.totalNum) {
                                return false;
                            }
                        });
                        //防止输入全部内容后用户拖动内容到输入框
                        self[0].ondrop = function() {
                            if (self.val().length >= base.totalNum) {
                                return false;
                            }
                        };
                        //防止输入全部内容后用户鼠标右键粘贴内容到输入框
                        self[0].onpaste = function() {
                            if (self.val().length >= base.totalNum && !getTextareaSelectVal()) {
                                return false;
                            }
                        };
                        //获取所选文本的开始和结束位置
                        function getPositions() {
                            var x = 0,
                            y = 0,
                            val = self[0].value;
                            x = self[0].selectionStart;
                            y = self[0].selectionEnd;
                            return {
                                "val": val,
                                "x": x,
                                "y": y
                            };
                        }
                        //获取textarea中选择的文本
                        function getTextareaSelectVal() {
                            if (window.getSelection) {
                                //Firefox,Chrome,Safari,opera etc
                                return getPositions().val.substring(getPositions().x, getPositions().y).length > 0;
                            } else if (document.selection) {
                                //IE,IE下可以直接获取,不必利用开始和结束位置截取
                                return document.selection.createRange().text.length > 0;
                            }
                        }
                    }
                }
                if (self.val() != "") {
                    self.prev("label").html("");
                }
            }
            base.checkWord(self[0], fun, 100);
        });
    },

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。