各位加了一天班累了吧?那我们来继续未完的表单验证吧

前言

接着昨天的写:工作了一个星期各位一定累了吧,那我们一起来表单验证一番吧!

因为昨天写到后面确实写不动了,就停下了,今天我们继续吧,看看能不能解决昨天提出的几个问题,由于今天看了下别人写的插件,在里面找到了一些很不错的点子,这里也应用起来了,慢慢改造我们的程序吧。

现状

经过今天的奋战,我们的界面变成了这个样子啦:

代码变成了这个样子:

js代码

/// <reference path="jquery-1.7.1.min.js" />
(function ($) {
    var FormValidator = function () {
        this.regexEnum = {
            idCard: /^[1-9]([0-9]{14}|[0-9]{16})([0-9]|X)$/,
            num: /^\-?([1-9]\d*)$/,         //数字
            email: /^([0-9A-Za-z\-_\.]+)@([0-9a-z]+\.[a-z]{2,3}(\.[a-z]{2})?)$/,
            phone: /^1[3|4|5|8]\d{9}$/,
            chinese: /^[\u0391-\uFFE5]{2,6}$/, //2-6位中文
            password: /^[a-zA-Z0-9_]{8,32}$/ //6-32位密码验证
        };
        this.validatorArr = {};
    };

    FormValidator.prototype.requred = function (el) {
        //        if (el.val() == '') {
        //            return false;
        //        }
        return true;
    }

    FormValidator.prototype.init = function () {
        var scope = this;
        $('.formValidate').each(function () {
            var el = $(this);
            scope.initItem(el);
        }); //each
    };

    FormValidator.prototype.initItem = function (el) {
        if (typeof el == 'string') el = $('#' + el);
        var scope = this;
        var cfg = el.attr('data-cfg');

        if (cfg && cfg.length > 0) {
            cfg = eval('(' + cfg + ')');
            //            cfg = JSON.parse(cfg);
            var check = cfg.check || true,
                id = el.attr('id') || new Date().getTime(),
                initMsg = cfg.initMsg || '请填入信息',
                sucMsg = cfg.sucMsg || '格式正确',
                errorMsg = cfg.errorMsg || '请注意格式',
                requred = cfg.requred || false,
                msgPosition = cfg.msgPosition || 'right';
            cfg.id = id;
            cfg.check = check;
            cfg.initMsg = initMsg;
            cfg.sucMsg = sucMsg;
            cfg.errorMsg = errorMsg;
            cfg.msgPosition = msgPosition;
            cfg.requred = requred;

            var tips = $('<div class="validateTips validateInit" id="' + id + 'Tips"><div class="triangle_icon"><div class="before"></div><div class="after"></div></div>' + initMsg + '</div>');
            //                var tips = $('<div class="validateTips validateInit" id="' + id + 'Tips">' + initMsg + '</div>');
            var offset = el.offset();
            var height = parseInt(el.outerHeight());
            var width = parseInt(el.outerWidth());
            var l = offset.left;
            var t = offset.top;

            if (msgPosition == 'bottom') {
                tips.addClass('posBottom');
                t += height + 4;
            } else if (msgPosition == 'right') {
                tips.addClass('posRight');
                l += width + 6;
            } else if (msgPosition == 'top') {
                tips.addClass('posTop');
                t += height * (-1) - 8;
            }
            tips.css({ left: l, top: t });
            $('body').append(tips);

            cfg.el = el;
            cfg.tipEl = tips;
            //该表单的验证
            cfg.validate = function () {
                scope.funcValidate(el, cfg);
            };

            //会触发验证的事件(取消验证后,该事件取消的话害怕引起事件丢失)
            el.change(function () {
                scope.funcValidate(el, cfg);
            });
            el.focus(function () {
                scope.funcValidate(el, cfg);
            });
            el.blur(function () {
                scope.funcValidate(el, cfg);
            });
            el.keyup(function () {
                scope.funcValidate(el, cfg);
            });
            el.keydown(function () {
                scope.funcValidate(el, cfg);
            });

            scope.validatorArr[id] = cfg; //生成相关验证对象

        } else {
            console.log('请输入完整验证信息!否则控件会产生错误!');
        }
    };

    FormValidator.prototype.funcValidate = function (el, cfg) {
        var id = cfg.id;
        var check = cfg.check; //判断是否开启验证
        //取消事件不执行下面逻辑
        if (!this.validatorArr[id])
            return false;
        //若是没有开启验证便忽略之
        if (!check) {
            this.validatorArr[id]['state'] = 'ignore';
            return false;
        }

        var type = cfg.type;
        var regexObj = cfg.regexObj; //正则相关
        var rangeObj = cfg.rangeObj; //范围验证
        var compareObj = cfg.compareObj; //范围验证

        var msg = '';
        var isPass = 1; //1成功,-1错误

        //首先进行非空验证
        if (cfg.requred) {
            if (el.val() == '') {
                isPass = -1;
                msg = '该项必填';
            }
        }

        //type优先
        if (isPass == 1 && el.val().length > 0 && typeof type == 'string') {
            var typeArr = type.split('|'); //可能包含验证组
            var errorArr = cfg.errorMsg.split('|'); //各个组错误时候对应的信息
            for (var i = 0, len = typeArr.length; i < len; i++) {
                var r = this.regexEnum[typeArr[i]];
                //测试通过
                if (r.test(el.val())) {
                    msg = cfg.sucMsg;
                } else {
                    isPass = -1;
                    msg = errorArr[i] ? errorArr[i] : cfg.errorMsg;
                    break; //一旦有错误的地方便中断
                }
            }
        }
        //当第一步验证通过便执行自身正则验证
        if (isPass == 1 && el.val().length > 0 && regexObj) {
            //当未指定type时候,便执行页面中的正则表达式对象
            var r = regexObj;
            if (r.test(el.val())) {
                msg = cfg.sucMsg;
            } else {
                isPass = -1;
                msg = cfg.errorMsg;
            }
        }

        //当第二步验证通过便执行范围验证
        if (isPass == 1 && el.val().length > 0 && rangeObj) {
            //日期验证暂时忽略
            var rangeArr = rangeObj.split('|');
            if (rangeArr.length == 3) {
                var _v = el.val();
                var _p1 = rangeArr[1];
                var _p2 = rangeArr[2];
                if (rangeArr[0] == 'num') {
                    _v = parseInt(el.val());
                    _p1 = parseInt(rangeArr[1]);
                    _p2 = parseInt(rangeArr[2]);
                }
                if (_v > _p1 && _v < _p2) {
                    msg = cfg.sucMsg;
                } else {
                    isPass = -1;
                    msg = '请填入' + rangeArr[0] + '到' + rangeArr[1] + '直接的数字';
                }
            } else {
                console.log('范围参数错误');
            }
        }

        //执行对比运算
        if (isPass == 1 && el.val().length > 0 && compareObj) {
            var compareArr = compareObj.split('|');
            if (compareArr.length == 3) {
                var _type = compareArr[0]
                var _id = compareArr[1];
                var _flag = compareArr[2];
                var _v = el.val();
                var _cv = $('#' + _id).val();
                if (_type == 'num') {
                    _v = parseInt(_v);
                    _cv = parseInt(_cv);
                }

                if (_flag == '<') {
                    if (_v < _cv) {
                        msg = cfg.sucMsg;
                    } else {
                        isPass = -1;
                        msg = '该值过大';
                    }
                }
                if (_flag == '>') {
                    if (_v > _cv) {
                        msg = cfg.sucMsg;
                    } else {
                        isPass = -1;
                        msg = '该值过小';
                    }
                }
                if (_flag == '=') {
                    if (_v == _cv) {
                        msg = cfg.sucMsg;
                    } else {
                        isPass = -1;
                        msg = '两次数据不一致';
                    }
                }
            } else {
                console.log('范围参数错误');
            }
        }
        if (msg == '') isPass = 0;
        if (isPass == 0) {
            this.validatorArr[id]['state'] = 'init';
            this.validatorArr[id]['tipEl'].removeClass('validateError');
            this.validatorArr[id]['tipEl'].removeClass('validateSuc');
            this.validatorArr[id]['tipEl'].addClass('validateInit');
            this.validatorArr[id]['tipEl'].html('<div class="triangle_icon"><div class="before"></div><div class="after"></div></div>' + cfg.initMsg);
        } else if (isPass == 1) {
            this.validatorArr[id]['state'] = 'success';
            this.validatorArr[id]['tipEl'].removeClass('validateError');
            this.validatorArr[id]['tipEl'].removeClass('validateInit');
            this.validatorArr[id]['tipEl'].addClass('validateSuc');
            this.validatorArr[id]['tipEl'].html('<div class="triangle_icon"><div class="before"></div><div class="after"></div></div>' + msg);
        } else if (isPass == -1) {
            this.validatorArr[id]['state'] = 'error';
            this.validatorArr[id]['tipEl'].removeClass('validateSuc');
            this.validatorArr[id]['tipEl'].removeClass('validateInit');
            this.validatorArr[id]['tipEl'].addClass('validateError');
            this.validatorArr[id]['tipEl'].html('<div class="triangle_icon"><div class="before"></div><div class="after"></div></div>' + msg);
        }

    };

    FormValidator.prototype.validatorAll = function () {
        for (var k in this.validatorArr) {
            var v = this.validatorArr[k];
            v.validate();
        }
    };

    FormValidator.prototype.removeValidator = function (id) {
        if (id && this.validatorArr[id]) {
            this.validatorArr[id].tipEl.remove(); //删除提示信息
            this.validatorArr[id]['check'] = false; //将其验证状态置为false 
            var s = '';
        }
    };

    FormValidator.prototype.addValidator = function (id) {
        var el = $('#' + id);
        this.initItem(el);
    };

    FormValidator.prototype.validatorState = function () {
        for (var k in this.validatorArr) {
            var v = this.validatorArr[k];
            if (v.state == 'error') {
                return false;
            }
        }
        return true;
    };

    window.FormValidator = FormValidator;

})(jQuery);
View Code

html代码

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <style type="text/css">
        .form div { height: 30px; line-height: 30px; margin: 5px; }
        .validateTips { min-width: 100px; border-radius: 2px; padding: 5px 10px; z-index: 500; position: absolute; font-size: 12px; }
        .validateInit { background: #FFFFE0; border: 1px solid #F7CE39; }
        .validateError { background: orange; border: 1px solid red; }
        .validateSuc { background: #79D62D; border: 1px solid Green; }
        .triangle_icon { position: absolute; }
        .triangle_icon div { border-style: solid; border-width: 6px; position: absolute; }
        
        /*上边提示*/
        .posTop .triangle_icon { width: 12px; height: 12px; bottom: -12px; }
        .posTop .triangle_icon .after { bottom: 1px; }
        .posTop .triangle_icon .after { border-bottom-color: transparent; border-right-color: transparent; border-left-color: transparent; }
        .posTop .triangle_icon .before { border-bottom-color: transparent; border-right-color: transparent; border-left-color: transparent; }
        
        /*右边提示*/
        .posRight .triangle_icon { width: 12px; height: 12px; left: -12px; }
        .posRight .triangle_icon .after { left: 1px; }
        .posRight .triangle_icon .after { border-top-color: transparent; border-bottom-color: transparent; border-left-color: transparent; }
        .posRight .triangle_icon .before { border-top-color: transparent; border-bottom-color: transparent; border-left-color: transparent; }
        
        /*下边提示*/
        .posBottom .triangle_icon { width: 12px; height: 12px; top: -12px; }
        .posBottom .triangle_icon .after { top: 1px; }
        .posBottom .triangle_icon .after { border-top-color: transparent; border-right-color: transparent; border-left-color: transparent; }
        .posBottom .triangle_icon .before { border-top-color: transparent; border-right-color: transparent; border-left-color: transparent; }
        
        
        /*初始化时候的皮肤*/
        .validateInit .before { border-color: #F7CE39; }
        .validateInit .after { border-color: #FFFFE0; }
        
        /*失败时候的皮肤*/
        .validateError .before { border-color: red; }
        .validateError .after { border-color: orange; }
        
        /*成功时候的皮肤*/
        .validateSuc .before { border-color: Green; }
        .validateSuc .after { border-color: #79D62D; }
        
        
        /*表单相关样式,和验证无关,可删除*/
        .form { width: 820px; margin: 0 auto; }
        
        .form ul { list-style-type: none; padding-left: 0; }
        
        .form li > label { display: inline-block; min-width: 200px; padding-right: 14px; text-align: right; vertical-align: -2px; }
        .form ul .text { display: inline-block; line-height: 40px; margin-left: 10px; }
        
        /*表单相关样式,和验证无关,可删除*/
    </style>
    <script src="js/jquery-1.7.1.min.js" type="text/javascript"></script>
    <script src="js/yexiaochai_formValidator.js" type="text/javascript"></script>
    <script type="text/javascript">
        $(document).ready(function () {
            var f = new FormValidator();
            f.init();
            f.validatorAll();

            var bt = $('#bt');
            var add = $('#add');
            var remove = $('#remove');
            var name = $('#name');
            var single = $('#single');

            bt.click(function () {
                f.validatorAll();
                var end = f.validatorState();
                var s = '';
            });

            single.click(function () {
                f.initItem(name.val());

                var s = '';
            });

            add.click(function () {
                f.addValidator(name.val());
                var s = '';
            });

            remove.click(function () {
                f.removeValidator(name.val());
                var s = '';
            });

        });
    </script>
</head>
<body>
    <div class="form">
        <input type="text" id="name" />
        <input type="button" value="取消验证" id="remove" />
        <input type="button" value="添加验证" id="add" />
        <input type="button" value="单项验证" id="single" />
        <ul>
            <li>
                <label>
                    身份证:</label>
                <div class="text">
                    <input type="text" id="idCard" class="formValidate" data-cfg="{ requred: true, type: 'idCard', msgPosition: 'right', initMsg: '请输入身份证号码!', sucMsg: '正确', errorMsg: '该项必填|格式错误'}" />
                </div>
            </li>
            <li>
                <label>
                    数字:
                </label>
                <div class="text">
                    <input type="text" id="num" class="formValidate" data-cfg="{ type: 'num'}" />
                </div>
            </li>
            <li>
                <label>
                    邮件:
                </label>
                <div class="text">
                    <input type="text" class="formValidate" data-cfg="{ type: 'email', initMsg: '请输入邮箱地址!'}" />
                </div>
            </li>
            <li>
                <label>
                    手机:
                </label>
                <div class="text">
                    <input type="text" class="formValidate" data-cfg="{ type: 'phone', initMsg: '请请输入手机号码!'}" />
                </div>
            </li>
            <li>
                <label>
                    QQ:
                </label>
                <div class="text">
                    <input type="text" class="formValidate" data-cfg="{ regexObj: /^[1-9]*[1-9][0-9]*$/, initMsg: '请请输入手机号码!'}" />
                </div>
            </li>
            <li>
                <label>
                    年龄要求(18-25):
                </label>
                <div class="text">
                    <input type="text" class="formValidate" data-cfg="{ rangeObj: 'num|18|25', initMsg: '请输入您的年龄'}" />
                </div>
            </li>
            <li>
                <label>
                    用户名:
                </label>
                <div class="text">
                    <input type="text" class="formValidate" />(ajax类型测试)
                </div>
            </li>
            <li>
                <label>
                    密码:
                </label>
                <div class="text">
                    <input type="text" id="pwd" class="formValidate" data-cfg="{ requred: true, type: 'password', msgPosition: 'right', initMsg: '请输入密码!', sucMsg: '正确', errorMsg: '格式错误'}" />
                </div>
            </li>
            <li>
                <label>
                    重复密码:
                </label>
                <div class="text">
                    <input type="text" id="rePwd" class="formValidate" data-cfg="{ compareObj: 'str|pwd|=', requred: true}" />(验证对比时,应该考虑日期,字符串,数字)
                </div>
            </li>
            <li>
                <label>
                    工作年限/薪水范围:
                </label>
                <div class="text">
                    <input type="text" id="d_start" class="formValidate" data-cfg="{ type: 'num', msgPosition: 'bottom'  }" />-
                    <input type="text" id="d_end" class="formValidate" data-cfg="{ compareObj: 'num|d_start|>',  type: 'num', msgPosition: 'bottom' }" />
                </div>
            </li>
            <li>
            <br />
            <br />
                <label>
                    学历:
                </label>
                <div class="text">
                    <select id="sec" class="formValidate" data-cfg="{ requred: true, initMsg: '请选择学历!', sucMsg: '正确', errorMsg: '必须选择学历' }">
                        <option value="" selected="selected">请选择</option>
                        <option value="1">专科</option>
                        <option value="2">本科</option>
                        <option value="3">硕士</option>
                    </select>
                </div>
            </li>
        </ul>
        <input type="button" value="提交" id="bt" />
    </div>
</body>
</html>
View Code

代码改着改着我自己都不认识了。。。

现在能实现的功能都实现了,但是心里那个没底啊!!!相当的没底!然后还有几个功能没有实现,比如多选框和单选框什么的,我这里就不做纠结了,因为是实验产物,工作也不一定用得上。

现在我们便一点一点的来看看这个代码,剥离他的外衣吧!

代码分析

为了更加清楚的阅读,我们把页面分离出来让他简单一点:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <style type="text/css">
        .form div { height: 30px; line-height: 30px; margin: 5px; }
        .validateTips { min-width: 100px; border-radius: 2px; padding: 5px 10px; z-index: 500; position: absolute; font-size: 12px; }
        .validateInit { background: #FFFFE0; border: 1px solid #F7CE39; }
        .validateError { background: orange; border: 1px solid red; }
        .validateSuc { background: #79D62D; border: 1px solid Green; }
        .triangle_icon { position: absolute; }
        .triangle_icon div { border-style: solid; border-width: 6px; position: absolute; }
        
        /*上边提示*/
        .posTop .triangle_icon { width: 12px; height: 12px; bottom: -12px; }
        .posTop .triangle_icon .after { bottom: 1px; }
        .posTop .triangle_icon .after { border-bottom-color: transparent; border-right-color: transparent; border-left-color: transparent; }
        .posTop .triangle_icon .before { border-bottom-color: transparent; border-right-color: transparent; border-left-color: transparent; }
        
        /*右边提示*/
        .posRight .triangle_icon { width: 12px; height: 12px; left: -12px; }
        .posRight .triangle_icon .after { left: 1px; }
        .posRight .triangle_icon .after { border-top-color: transparent; border-bottom-color: transparent; border-left-color: transparent; }
        .posRight .triangle_icon .before { border-top-color: transparent; border-bottom-color: transparent; border-left-color: transparent; }
        
        /*下边提示*/
        .posBottom .triangle_icon { width: 12px; height: 12px; top: -12px; }
        .posBottom .triangle_icon .after { top: 1px; }
        .posBottom .triangle_icon .after { border-top-color: transparent; border-right-color: transparent; border-left-color: transparent; }
        .posBottom .triangle_icon .before { border-top-color: transparent; border-right-color: transparent; border-left-color: transparent; }
        
        
        /*初始化时候的皮肤*/
        .validateInit .before { border-color: #F7CE39; }
        .validateInit .after { border-color: #FFFFE0; }
        
        /*失败时候的皮肤*/
        .validateError .before { border-color: red; }
        .validateError .after { border-color: orange; }
        
        /*成功时候的皮肤*/
        .validateSuc .before { border-color: Green; }
        .validateSuc .after { border-color: #79D62D; }
        
        
        /*表单相关样式,和验证无关,可删除*/
        .form { width: 820px; margin: 0 auto; }
        
        .form ul { list-style-type: none; padding-left: 0; }
        
        .form li > label { display: inline-block; min-width: 200px; padding-right: 14px; text-align: right; vertical-align: -2px; }
        .form ul .text { display: inline-block; line-height: 40px; margin-left: 10px; }
        
        /*表单相关样式,和验证无关,可删除*/
    </style>
    <script src="js/jquery-1.7.1.min.js" type="text/javascript"></script>
    <script src="js/yexiaochai_formValidator.js" type="text/javascript"></script>
    <script type="text/javascript">
        $(document).ready(function () {
            var f = new FormValidator();
            f.init();
            f.validatorAll();

            var bt = $('#bt');
            var add = $('#add');
            var remove = $('#remove');
            var name = $('#name');
            var single = $('#single');

            bt.click(function () {
                f.validatorAll();
                var end = f.validatorState();
                var s = '';
            });

            single.click(function () {
                f.initItem(name.val());
                var s = '';
            });

            add.click(function () {
                f.addValidator(name.val());
                var s = '';
            });

            remove.click(function () {
                f.removeValidator(name.val());
                var s = '';
            });

        });
    </script>
</head>
<body>
    <div class="form">
        <input type="text" id="name" />
        <input type="button" value="取消验证" id="remove" />
        <input type="button" value="添加验证" id="add" />
        <input type="button" value="单项验证" id="single" />
        <ul>
            <li>
                <label>
                    用户名:
                </label>
                <div class="text">
                    <input type="text" class="formValidate" data-cfg="{ ajaxObj: 'ajax.ashx|name', requred: true, type: 'chinese', initMsg: '请输入中文名!', sucMsg: '名字正确', errorMsg: '格式错误' }" />(ajax类型测试)
                </div>
            </li>
        </ul>
        <input type="button" value="提交" id="bt" />
    </div>
</body>
</html>
View Code

我们就看着一个文本框的验证,这里我们来捋一捋这个js代码先:

1 定义构造函数

 1 var FormValidator = function () {
 2     this.regexEnum = {
 3         idCard: /^[1-9]([0-9]{14}|[0-9]{16})([0-9]|X)$/,
 4         num: /^\-?([1-9]\d*)$/,         //数字
 5         email: /^([0-9A-Za-z\-_\.]+)@([0-9a-z]+\.[a-z]{2,3}(\.[a-z]{2})?)$/,
 6         phone: /^1[3|4|5|8]\d{9}$/,
 7         chinese: /^[\u0391-\uFFE5]{2,6}$/, //2-6位中文
 8         password: /^[a-zA-Z0-9_]{8,32}$/ //6-32位密码验证
 9     };
10     this.validatorArr = {};
11 };

这里定义了一个构造函数,我们这里每次验证时候都必须初始化一次(new一次),其中第十行是保存其所有项目的验证对象(这里取名为数组其实是个错误)。

我们来看看这个regexEnum对象包含了我们常用的正则验证,若是不能满足条件可以再加,于是这里就有一个问题:

我们的regexEnum其实是可以共用的,这里写到这里其实不合适,我们应该将其提出来,但我这里先暂时不管他了。

2 初始化各个表单项

1 FormValidator.prototype.init = function () {
2     var scope = this;
3     $('.formValidate').each(function () {
4         var el = $(this);
5         scope.initItem(el);
6     }); //each
7 };

这个方法用于遍历具有“formValidate”的class的标签,并单个对其初始化:

 1 FormValidator.prototype.initItem = function (el) {
 2     if (typeof el == 'string') el = $('#' + el);
 3     var scope = this;
 4     var cfg = el.attr('data-cfg');
 5 
 6     if (cfg && cfg.length > 0) {
 7         cfg = eval('(' + cfg + ')');
 8         //            cfg = JSON.parse(cfg);
 9         var check = cfg.check || true,
10             id = el.attr('id') || new Date().getTime(),
11             initMsg = cfg.initMsg || '请填入信息',
12             sucMsg = cfg.sucMsg || '格式正确',
13             errorMsg = cfg.errorMsg || '请注意格式',
14             requred = cfg.requred || false,
15             msgPosition = cfg.msgPosition || 'right';
16         cfg.id = id;
17         cfg.check = check;
18         cfg.initMsg = initMsg;
19         cfg.sucMsg = sucMsg;
20         cfg.errorMsg = errorMsg;
21         cfg.msgPosition = msgPosition;
22         cfg.requred = requred;
23 
24         var tips = $('<div class="validateTips validateInit" id="' + id + 'Tips"><div class="triangle_icon"><div class="before"> </div><div class="after"></div></div>' + initMsg + '</div>');
25         //                var tips = $('<div class="validateTips validateInit" id="' + id + 'Tips">' + initMsg + '</div>');
26         var offset = el.offset();
27         var height = parseInt(el.outerHeight());
28         var width = parseInt(el.outerWidth());
29         var l = offset.left;
30         var t = offset.top;
31 
32         if (msgPosition == 'bottom') {
33             tips.addClass('posBottom');
34             t += height + 4;
35         } else if (msgPosition == 'right') {
36             tips.addClass('posRight');
37             l += width + 6;
38         } else if (msgPosition == 'top') {
39             tips.addClass('posTop');
40             t += height * (-1) - 8;
41         }
42         tips.css({ left: l, top: t });
43         $('body').append(tips);
44 
45         cfg.el = el;
46         cfg.tipEl = tips;
47         //该表单的验证
48         cfg.validate = function () {
49             scope.funcValidate(el, cfg);
50         };
51 
52         //会触发验证的事件(取消验证后,该事件取消的话害怕引起事件丢失)
53         el.change(function () {
54             scope.funcValidate(el, cfg);
55         });
56         el.focus(function () {
57             scope.funcValidate(el, cfg);
58         });
59         el.blur(function () {
60             scope.funcValidate(el, cfg);
61         });
62         el.keyup(function () {
63             scope.funcValidate(el, cfg);
64         });
65         el.keydown(function () {
66             scope.funcValidate(el, cfg);
67         });
68 
69         scope.validatorArr[id] = cfg; //生成相关验证对象

70 
71     } else {
72         console.log('请输入完整验证信息!否则控件会产生错误!');
73     }
74 };

这个方法是用于初始化单个标签的,他的代码很长,但是却很简单,主要干了这么几件事情:

① 生成提示框,并定位到文本框附近

② 为文本框绑定各种事件,用于验证时候使用

③ 初始化validatorArr对象,将单项装入

这个方法就干了这些事情,但是他是有一定问题的,我们这里首先看看其将触发哪些事件:

 1 //会触发验证的事件(取消验证后,该事件取消的话害怕引起事件丢失)
 2         el.change(function () {
 3             scope.funcValidate(el, cfg);
 4         });
 5         el.focus(function () {
 6             scope.funcValidate(el, cfg);
 7         });
 8         el.blur(function () {
 9             scope.funcValidate(el, cfg);
10         });
11         el.keyup(function () {
12             scope.funcValidate(el, cfg);
13         });
14         el.keydown(function () {
15             scope.funcValidate(el, cfg);
16         });

这段代码本身没有什么问题,用于一般的验证还是可以接受的,但是若是用于ajax的话,不幸的事实告诉我他请求发的太多了。。。。

但是,我们这里又必须保证他的及时性,感觉还不能延迟请求呢,所以我这里去掉了两个事件:

        el.blur(function () {
            scope.funcValidate(el, cfg);
        });
        el.keydown(function () {
            scope.funcValidate(el, cfg);
        });

所以原来就保留了三个事件,应该也够了吧。

然后这里调用了一个funcValidate(el, cfg)方法,用于单个验证,并改变提示信息,但是我们这里又有一个问题:

PS:细细读起来怎么这么多问题呢?汗。。。

这里,传的参数el是多余的,因为cfg里面就包含了这个东西,所以我们把它去掉。于是我们接着往下走:

FormValidator.prototype.funcValidate = function (cfg) {
    var id = cfg.id;
    var el = cfg.el;
    var check = cfg.check; //判断是否开启验证
    var that = this;

    //取消事件不执行下面逻辑
    if (!this.validatorArr[id])
        return false;
    //若是没有开启验证便忽略之
    if (!check) {
        this.validatorArr[id]['state'] = 'ignore';
        return false;
    }

    var type = cfg.type;
    var regexObj = cfg.regexObj; //正则相关
    var rangeObj = cfg.rangeObj; //范围验证
    var compareObj = cfg.compareObj; //范围验证
    var ajaxObj = cfg.ajaxObj; //ajax验证

    var msg = '';
    var isPass = 1; //1成功,-1错误

    //首先进行非空验证
    if (cfg.requred) {
        if (el.val() == '') {
            isPass = -1;
            msg = '该项必填';
        }
    }

    //type优先
    if (isPass == 1 && el.val().length > 0 && typeof type == 'string') {
        var typeArr = type.split('|'); //可能包含验证组
        var errorArr = cfg.errorMsg.split('|'); //各个组错误时候对应的信息
        for (var i = 0, len = typeArr.length; i < len; i++) {
            var r = this.regexEnum[typeArr[i]];
            //测试通过
            if (r.test(el.val())) {
                msg = cfg.sucMsg;
            } else {
                isPass = -1;
                msg = errorArr[i] ? errorArr[i] : cfg.errorMsg;
                break; //一旦有错误的地方便中断
            }

        }
    }
    //当第一步验证通过便执行自身正则验证
    if (isPass == 1 && el.val().length > 0 && regexObj) {
        //当未指定type时候,便执行页面中的正则表达式对象
        var r = regexObj;
        if (r.test(el.val())) {
            msg = cfg.sucMsg;
        } else {
            isPass = -1;
            msg = cfg.errorMsg;
        }
    }

    //当第二步验证通过便执行范围验证
    if (isPass == 1 && el.val().length > 0 && rangeObj) {
        //日期验证暂时忽略
        var rangeArr = rangeObj.split('|');
        if (rangeArr.length == 3) {
            var _v = el.val();
            var _p1 = rangeArr[1];
            var _p2 = rangeArr[2];
            if (rangeArr[0] == 'num') {
                _v = parseInt(el.val());
                _p1 = parseInt(rangeArr[1]);
                _p2 = parseInt(rangeArr[2]);
            }
            if (_v > _p1 && _v < _p2) {
                msg = cfg.sucMsg;
            } else {
                isPass = -1;
                msg = '请填入' + rangeArr[0] + '到' + rangeArr[1] + '直接的数字';
            }
        } else {
            console.log('范围参数错误');
        }
    }

    //执行对比运算
    if (isPass == 1 && el.val().length > 0 && compareObj) {
        var compareArr = compareObj.split('|');
        if (compareArr.length == 3) {
            var _type = compareArr[0]
            var _id = compareArr[1];
            var _flag = compareArr[2];
            var _v = el.val();
            var _cv = $('#' + _id).val();
            if (_type == 'num') {
                _v = parseInt(_v);
                _cv = parseInt(_cv);
            }

            if (_flag == '<') {
                if (_v < _cv) {
                    msg = cfg.sucMsg;
                } else {
                    isPass = -1;
                    msg = '该值过大';
                }
            }
            if (_flag == '>') {
                if (_v > _cv) {
                    msg = cfg.sucMsg;
                } else {
                    isPass = -1;
                    msg = '该值过小';
                }
            }
            if (_flag == '=') {
                if (_v == _cv) {
                    msg = cfg.sucMsg;
                } else {
                    isPass = -1;
                    msg = '两次数据不一致';
                }
            }
        } else {
            console.log('范围参数错误');
        }
    }

    //ajax验证
    if (isPass == 1 && el.val().length > 0 && ajaxObj) {
        var ajaxArr = ajaxObj.split('|');
        if (ajaxArr.length == 2) {
            var _url = ajaxArr[0];
            var _param = {};
            _param[ajaxArr[1]] = el.val();
            $.get(_url, _param, function (data) {
                if (typeof data == 'string')
                    data = eval('(' + data + ')');
                if (data.retcode == '0') {
                    msg = cfg.sucMsg;
                } else {
                    isPass = -1;
                    msg = data.msg;
                }
                setResult(that, isPass);
            });
            return false; //在此中断后续操作
        }
    }

    setResult(this, isPass);

    function setResult(scope, isPass) {
        if (msg == '') isPass = 0;
        if (isPass == 0) {
            scope.validatorArr[id]['state'] = 'init';
            scope.validatorArr[id]['tipEl'].removeClass('validateError');
            scope.validatorArr[id]['tipEl'].removeClass('validateSuc');
            scope.validatorArr[id]['tipEl'].addClass('validateInit');
            scope.validatorArr[id]['tipEl'].html('<div class="triangle_icon"><div class="before"></div>
<div class="after"></div></div>' + cfg.initMsg);
        } else if (isPass == 1) {
            scope.validatorArr[id]['state'] = 'success';
            scope.validatorArr[id]['tipEl'].removeClass('validateError');
            scope.validatorArr[id]['tipEl'].removeClass('validateInit');
            scope.validatorArr[id]['tipEl'].addClass('validateSuc');
            scope.validatorArr[id]['tipEl'].html('<div class="triangle_icon"><div class="before"></div>
<div class="after"></div></div>' + msg);
        } else if (isPass == -1) {
            scope.validatorArr[id]['state'] = 'error';
            scope.validatorArr[id]['tipEl'].removeClass('validateSuc');
            scope.validatorArr[id]['tipEl'].removeClass('validateInit');
            scope.validatorArr[id]['tipEl'].addClass('validateError');
            scope.validatorArr[id]['tipEl'].html('<div class="triangle_icon"><div class="before"></div>
<div class="after"></div></div>' + msg);
        }
    }
};
View Code

这个家伙里面包含了很多东西,看着庞大,其实没有什么意义,我们来一点一点的将之剥离:

    var type = cfg.type;
    var regexObj = cfg.regexObj; //正则相关
    var rangeObj = cfg.rangeObj; //范围验证
    var compareObj = cfg.compareObj; //范围验证
    var ajaxObj = cfg.ajaxObj; //ajax验证

    var msg = '';
    var isPass = 1; //0初始化,1成功,-1错误

首先,我们将配置文件中的东东给读出来,说是配置文件,其实就是写在标签里面的自定义属性了:

<input type="text" class="formValidate" data-cfg="{ ajaxObj: 'ajax.ashx|name', requred: true,  type: 'chinese', initMsg: '请输入中文名!', sucMsg: '名字正确', errorMsg: '格式错误' }" />

① 我这里验证的顺序首先第一步就是判断是否必填,若是该步不能通过,下面的逻辑都不用走了:

1 //首先进行非空验证
2 if (cfg.requred) {
3     if (el.val() == '') {
4         isPass = -1;
5         msg = '该项必填';
6     }
7 }

PS:这里的提示信息想要更人性话需要在配置文件里面做更多的事情了

② 若是必填这块通过了,我们就优先判断type指定的类型(其实就是我们已经定义好的正则了):

 1 //type优先
 2 if (isPass == 1 && el.val().length > 0 && typeof type == 'string') {
 3     var typeArr = type.split('|'); //可能包含验证组
 4     var errorArr = cfg.errorMsg.split('|'); //各个组错误时候对应的信息
 5     for (var i = 0, len = typeArr.length; i < len; i++) {
 6         var r = this.regexEnum[typeArr[i]];
 7         //测试通过
 8         if (r.test(el.val())) {
 9             msg = cfg.sucMsg;
10         } else {
11             isPass = -1;
12             msg = errorArr[i] ? errorArr[i] : cfg.errorMsg;
13             break; //一旦有错误的地方便中断
14         }
15     }
16 }

在这里有一个技巧,因为开始我认为可能不止判断一个东西,会有多个需要判断的地方,比如必填与数字限制的组合:

所以我就在type中使用“|”分割id,做多次验证,但是!!!我后面的做法直接导致了这里的做法没有意义啦!所以这里去掉吧。

③ 若是发现我提供的正则不能满足要求,便可以自己写正则了:

 1 //当第一步验证通过便执行自身正则验证
 2 if (isPass == 1 && el.val().length > 0 && regexObj) {
 3     //当未指定type时候,便执行页面中的正则表达式对象
 4     var r = regexObj;
 5     if (r.test(el.val())) {
 6         msg = cfg.sucMsg;
 7     } else {
 8         isPass = -1;
 9         msg = cfg.errorMsg;
10     }
11 }

④ 若是以上通过的话,我们便进入下一个东西,验证数字范围:

 1 //当第二步验证通过便执行范围验证
 2 if (isPass == 1 && el.val().length > 0 && rangeObj) {
 3     //日期验证暂时忽略
 4     var rangeArr = rangeObj.split('|');
 5     if (rangeArr.length == 3) {
 6         var _v = el.val();
 7         var _p1 = rangeArr[1];
 8         var _p2 = rangeArr[2];
 9         if (rangeArr[0] == 'num') {
10             _v = parseInt(el.val());
11             _p1 = parseInt(rangeArr[1]);
12             _p2 = parseInt(rangeArr[2]);
13         }
14         if (_v > _p1 && _v < _p2) {
15             msg = cfg.sucMsg;
16         } else {
17             isPass = -1;
18             msg = '请填入' + rangeArr[0] + '到' + rangeArr[1] + '直接的数字';
19         }
20     } else {
21         console.log('范围参数错误');
22     }
23 }

PS:这里只验证了数字和字符串(字符)的范围,其实还想验证日期来着,但是这块有点大,给放弃了,看看后面有必要加上没。。。 ⑤ 范围验证结束后便进入我们的相等与否的验证,典型引用就是密码相同验证,工资区间验证(后面的大于前面的)

 1 //执行对比运算
 2 if (isPass == 1 && el.val().length > 0 && compareObj) {
 3     var compareArr = compareObj.split('|');
 4     if (compareArr.length == 3) {
 5         var _type = compareArr[0]
 6         var _id = compareArr[1];
 7         var _flag = compareArr[2];
 8         var _v = el.val();
 9         var _cv = $('#' + _id).val();
10         if (_type == 'num') {
11             _v = parseInt(_v);
12             _cv = parseInt(_cv);
13         }
14 
15         if (_flag == '<') {
16             if (_v < _cv) {
17                 msg = cfg.sucMsg;
18             } else {
19                 isPass = -1;
20                 msg = '该值过大';
21             }
22         }
23         if (_flag == '>') {
24             if (_v > _cv) {
25                 msg = cfg.sucMsg;
26             } else {
27                 isPass = -1;
28                 msg = '该值过小';
29             }
30         }
31         if (_flag == '=') {
32             if (_v == _cv) {
33                 msg = cfg.sucMsg;
34             } else {
35                 isPass = -1;
36                 msg = '两次数据不一致';
37             }
38         }
39     } else {
40         console.log('范围参数错误');
41     }
42 }

这里的提示信息,不够友好,若是想自定义还是得在自定义属性中做手脚,我这里就不管他了。

⑥ 最后一步,若是前面的验证都通过,好吧,无奈我们必须进行ajax类型的验证(验证用户名在数据库是否存在):

 1 //ajax验证
 2 if (isPass == 1 && el.val().length > 0 && ajaxObj) {
 3     var ajaxArr = ajaxObj.split('|');
 4     if (ajaxArr.length == 2) {
 5         var _url = ajaxArr[0];
 6         var _param = {};
 7         _param[ajaxArr[1]] = el.val();
 8         $.get(_url, _param, function (data) {
 9             if (typeof data == 'string')
10                 data = eval('(' + data + ')');
11             if (data.retcode == '0') {
12                 msg = cfg.sucMsg;
13             } else {
14                 isPass = -1;
15                 msg = data.msg;
16             }
17             setResult(that, isPass);
18         });
19         return false; //在此中断后续操作
20     }
21 }

注意:我这里对后端传来的格式做了强制性的约束:

{retcode: 0, msg: '', data: null}
必须是这个样子的,retcode为0就是正确的情况,否则便读取msg的数据
注意,msg可能会是数组,我这里还有一定问题

以上便是完整的验证逻辑,完了文本类型的验证估计也差不多了,最后附上验证后面的函数:

 1 function setResult(scope, isPass) {
 2     if (msg == '') isPass = 0;
 3     if (isPass == 0) {
 4         scope.validatorArr[id]['state'] = 'init';
 5         scope.validatorArr[id]['tipEl'].removeClass('validateError');
 6         scope.validatorArr[id]['tipEl'].removeClass('validateSuc');
 7         scope.validatorArr[id]['tipEl'].addClass('validateInit');
 8         scope.validatorArr[id]['tipEl'].html('<div class="triangle_icon"><div class="before"></div><div class="after"></div></div>' + cfg.initMsg);
 9     } else if (isPass == 1) {
10         scope.validatorArr[id]['state'] = 'success';
11         scope.validatorArr[id]['tipEl'].removeClass('validateError');
12         scope.validatorArr[id]['tipEl'].removeClass('validateInit');
13         scope.validatorArr[id]['tipEl'].addClass('validateSuc');
14         scope.validatorArr[id]['tipEl'].html('<div class="triangle_icon"><div class="before"></div><div class="after"></div></div>' + msg);
15     } else if (isPass == -1) {
16         scope.validatorArr[id]['state'] = 'error';
17         scope.validatorArr[id]['tipEl'].removeClass('validateSuc');
18         scope.validatorArr[id]['tipEl'].removeClass('validateInit');
19         scope.validatorArr[id]['tipEl'].addClass('validateError');
20         scope.validatorArr[id]['tipEl'].html('<div class="triangle_icon"><div class="before"></div><div class="after"></div></div>' + msg);
21     }
22 }

我这里有个处理的不是很好的地方就是,就算验证通过我也没处理提示框,没有让其隐藏,特别是上下排列的场景,若是提示框遮住了文本框就非常不友好!

注意事项

最后,需要注意一点的就是,我们上面的cfg中还有一个check属性,该属性用于取消某个验证项的验证,所以我们在上面会看到这段代码:

 //若是没有开启验证便忽略之
        if (!check) {
            this.validatorArr[id]['state'] = 'ignore';
            return false;
        }

至此,整个主体逻辑就说完了,我们再看看其它功能。

方法描述

我们调用init方法只是将整个表单初始化,但是并没有验证,比如我们在编辑页面时候进入时希望看到验证后的结果,于是有了以下代码:

1 FormValidator.prototype.validatorAll = function () {
2     for (var k in this.validatorArr) {
3         var v = this.validatorArr[k];
4         v.validate();
5     }
6 };

添加移除某项的验证

在复杂的表单中,比如有些项目是隐藏的,当我们展开时候我们希望验证他,但是他隐藏时候我们希望不去验证他,所以我们有了以下代码,初始化不希望验证的话需要加上check: false属性。

 1 FormValidator.prototype.removeValidator = function (id) {
 2     if (id && this.validatorArr[id]) {
 3         this.validatorArr[id].tipEl.remove(); //删除提示信息
 4         this.validatorArr[id]['check'] = false; //将其验证状态置为false 
 5         var s = '';
 6     }
 7 };
 8 
 9 FormValidator.prototype.addValidator = function (id) {
10     var el = $('#' + id);
11     this.initItem(el);
12 };

获取验证状态

在我们点击提交表单时候,需要获取整个表单的验证状态,所以有了以下代码:

1 FormValidator.prototype.validatorState = function () {
2     for (var k in this.validatorArr) {
3         var v = this.validatorArr[k];
4         if (v.state == 'error') {
5             return false;
6         }
7     }
8     return true;
9 };

最后,边说便改,于是我们的代码就成了这个样子了。。。好了接下来我们来试试他灵不灵呢?

弱弱的验证成果

我们就先在一个文本上验证下我们的东西吧,因为代码太长,我暂时去掉CSS代码:

 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 2 <html xmlns="http://www.w3.org/1999/xhtml">
 3 <head>
 4     <title></title>
 5     <script src="js/jquery-1.7.1.min.js" type="text/javascript"></script>
 6     <script src="js/yexiaochai_formValidator.js" type="text/javascript"></script>
 7     <script type="text/javascript">
 8         $(document).ready(function () {
 9             var f = new FormValidator();
10             f.init();
11             f.validatorAll();
12 
13             var bt = $('#bt');
14             var add = $('#add');
15             var remove = $('#remove');
16             var name = $('#name');
17             var single = $('#single');
18 
19             bt.click(function () {
20                 f.validatorAll();
21                 var end = f.validatorState();
22                 var s = '';
23             });
24 
25             single.click(function () {
26                 f.initItem(name.val());
27                 var s = '';
28             });
29 
30             add.click(function () {
31                 f.addValidator(name.val());
32                 var s = '';
33             });
34 
35             remove.click(function () {
36                 f.removeValidator(name.val());
37                 var s = '';
38             });
39 
40         });
41     </script>
42 </head>
43 <body>
44     <div class="form">
45         <input type="text" id="name" />
46         <input type="button" value="取消验证" id="remove" />
47         <input type="button" value="添加验证" id="add" />
48         <input type="button" value="单项验证" id="single" />
49         <ul>
50             <li>
51                 <label>
52                     用户名:
53                 </label>
54                 <div class="text">
55                     <input type="text" class="formValidate" data-cfg="{ ajaxObj: 'ajax.ashx|name', requred: true, type: 'chinese', 
initMsg: '请输入中文名!', sucMsg: '名字正确', errorMsg: '格式错误' }"
/>(ajax类型测试) 56 </div> 57 </li> 58 </ul> 59 <input type="button" value="提交" id="bt" /> 60 </div> 61 </body> 62 </html>

我们这个名字的验证有以下几个:

1 非空验证

2 必须是中文

3 ajax验证

对比下他的配置信息,我们看看运行结果:

我们看到一来,我们就已经验证了,于是我们去掉其第十一行代码:

我们看到他只是初始化了,并未进行验证,于是我们试试取消验证与添加验证(输入文本框id即可)

取消验证后再点击提交,其验证状态便是忽略状态了:

当添加验证后便还原了。

最后看看我们只是为了一个名字验证干了这些事情:

ajax的代码

 public void ProcessRequest(HttpContext context)
    {
        context.Response.ContentType = "text/html";
        string[] arr_name = { "叶小钗", "一页书", "素还真" };
        var name = context.Request.QueryString["name"];

        var exist = false;
        foreach (string v in arr_name)
        {
            if (v == name)
            {
                exist = true; break;
            }
        }
        string retcode = exist ? "-1" : "0";
        string msg = exist ? "该用户名已经存在" : "";
        context.Response.Write("{retcode: " + retcode + ", msg: '" + msg + "', data: null}");
    }

这个东西只能用恐怖来形容吧!

完整的例子

最后我们来张完整的例子结束吧:

完整的HTML代码:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <style type="text/css">
        .form div { height: 30px; line-height: 30px; margin: 5px; }
        .validateTips { min-width: 100px; border-radius: 2px; padding: 5px 10px; z-index: 500; position: absolute; font-size: 12px; }
        .validateInit { background: #FFFFE0; border: 1px solid #F7CE39; }
        .validateError { background: orange; border: 1px solid red; }
        .validateSuc { background: #79D62D; border: 1px solid Green; }
        .triangle_icon { position: absolute; }
        .triangle_icon div { border-style: solid; border-width: 6px; position: absolute; }
        
        /*上边提示*/
        .posTop .triangle_icon { width: 12px; height: 12px; bottom: -12px; }
        .posTop .triangle_icon .after { bottom: 1px; }
        .posTop .triangle_icon .after { border-bottom-color: transparent; border-right-color: transparent; border-left-color: transparent; }
        .posTop .triangle_icon .before { border-bottom-color: transparent; border-right-color: transparent; border-left-color: transparent; }
        
        /*右边提示*/
        .posRight .triangle_icon { width: 12px; height: 12px; left: -12px; }
        .posRight .triangle_icon .after { left: 1px; }
        .posRight .triangle_icon .after { border-top-color: transparent; border-bottom-color: transparent; border-left-color: transparent; }
        .posRight .triangle_icon .before { border-top-color: transparent; border-bottom-color: transparent; border-left-color: transparent; }
        
        /*下边提示*/
        .posBottom .triangle_icon { width: 12px; height: 12px; top: -12px; }
        .posBottom .triangle_icon .after { top: 1px; }
        .posBottom .triangle_icon .after { border-top-color: transparent; border-right-color: transparent; border-left-color: transparent; }
        .posBottom .triangle_icon .before { border-top-color: transparent; border-right-color: transparent; border-left-color: transparent; }
        
        
        /*初始化时候的皮肤*/
        .validateInit .before { border-color: #F7CE39; }
        .validateInit .after { border-color: #FFFFE0; }
        
        /*失败时候的皮肤*/
        .validateError .before { border-color: red; }
        .validateError .after { border-color: orange; }
        
        /*成功时候的皮肤*/
        .validateSuc .before { border-color: Green; }
        .validateSuc .after { border-color: #79D62D; }
        
        
        /*表单相关样式,和验证无关,可删除*/
        .form { width: 820px; margin: 0 auto; }
        
        .form ul { list-style-type: none; padding-left: 0; }
        
        .form li > label { display: inline-block; min-width: 200px; padding-right: 14px; text-align: right; vertical-align: -2px; }
        .form ul .text { display: inline-block; line-height: 40px; margin-left: 10px; }
        
        /*表单相关样式,和验证无关,可删除*/
    </style>
    <script src="js/jquery-1.7.1.min.js" type="text/javascript"></script>
    <script src="js/yexiaochai_formValidator.js" type="text/javascript"></script>
    <script type="text/javascript">
        $(document).ready(function () {
            var f = new FormValidator();
            f.init();
            f.validatorAll();

            var bt = $('#bt');
            var add = $('#add');
            var remove = $('#remove');
            var name = $('#name');
            var single = $('#single');

            bt.click(function () {
                f.validatorAll();
                var end = f.validatorState();
                var s = '';
            });

            single.click(function () {
                f.initItem(name.val());

                var s = '';
            });

            add.click(function () {
                f.addValidator(name.val());
                var s = '';
            });

            remove.click(function () {
                f.removeValidator(name.val());
                var s = '';
            });

        });
    </script>
</head>
<body>
    <div class="form">
        <input type="text" id="name" />
        <input type="button" value="取消验证" id="remove" />
        <input type="button" value="添加验证" id="add" />
        <input type="button" value="单项验证" id="single" />
        <ul>
            <li>
                <label>
                    身份证:</label>
                <div class="text">
                    <input type="text" id="idCard" class="formValidate" data-cfg="{ requred: true, type: 'idCard', msgPosition: 'right', initMsg: '请输入身份证号码!', sucMsg: '正确', errorMsg: '该项必填|格式错误'}" />
                </div>
            </li>
            <li>
                <label>
                    数字:
                </label>
                <div class="text">
                    <input type="text" id="num" class="formValidate" data-cfg="{ type: 'num'}" />
                </div>
            </li>
            <li>
                <label>
                    邮件:
                </label>
                <div class="text">
                    <input type="text" class="formValidate" data-cfg="{ type: 'email', initMsg: '请输入邮箱地址!'}" />
                </div>
            </li>
            <li>
                <label>
                    手机:
                </label>
                <div class="text">
                    <input type="text" class="formValidate" data-cfg="{ type: 'phone', initMsg: '请请输入手机号码!'}" />
                </div>
            </li>
            <li>
                <label>
                    QQ:
                </label>
                <div class="text">
                    <input type="text" class="formValidate" data-cfg="{ regexObj: /^[1-9]*[1-9][0-9]*$/, initMsg: '请请输入手机号码!'}" />
                </div>
            </li>
            <li>
                <label>
                    年龄要求(18-25):
                </label>
                <div class="text">
                    <input type="text" class="formValidate" data-cfg="{ rangeObj: 'num|18|25', initMsg: '请输入您的年龄'}" />
                </div>
            </li>
            <li>
                <label>
                    用户名:
                </label>
                <div class="text">
                    <input type="text" class="formValidate" data-cfg="{ ajaxObj: 'ajax.ashx|name', requred: true, type: 'chinese', initMsg: '请输入中文名!', sucMsg: '名字正确', errorMsg: '格式错误' }"/>(ajax类型测试)
                </div>
            </li>
            <li>
                <label>
                    密码:
                </label>
                <div class="text">
                    <input type="text" id="pwd" class="formValidate" data-cfg="{ requred: true, type: 'password', msgPosition: 'right', initMsg: '请输入密码!', sucMsg: '正确', errorMsg: '格式错误'}" />
                </div>
            </li>
            <li>
                <label>
                    重复密码:
                </label>
                <div class="text">
                    <input type="text" id="rePwd" class="formValidate" data-cfg="{ compareObj: 'str|pwd|=', requred: true}" />(验证对比时,应该考虑日期,字符串,数字)
                </div>
            </li>
            <li>
                <label>
                    工作年限/薪水范围:
                </label>
                <div class="text">
                    <input type="text" id="d_start" class="formValidate" data-cfg="{ type: 'num', msgPosition: 'bottom'  }" />-
                    <input type="text" id="d_end" class="formValidate" data-cfg="{ compareObj: 'num|d_start|>',  type: 'num', msgPosition: 'bottom' }" />
                </div>
            </li>
            <li>
            <br />
            <br />
                <label>
                    学历:
                </label>
                <div class="text">
                    <select id="sec" class="formValidate" data-cfg="{ requred: true, initMsg: '请选择学历!', sucMsg: '正确', errorMsg: '必须选择学历' }">
                        <option value="" selected="selected">请选择</option>
                        <option value="1">专科</option>
                        <option value="2">本科</option>
                        <option value="3">硕士</option>
                    </select>
                </div>
            </li>
        </ul>
        <input type="button" value="提交" id="bt" />
    </div>
</body>
</html>
View Code

完整的JS代码

/// <reference path="jquery-1.7.1.min.js" />
(function ($) {
    var FormValidator = function () {
        this.regexEnum = {
            idCard: /^[1-9]([0-9]{14}|[0-9]{16})([0-9]|X)$/,
            num: /^\-?([1-9]\d*)$/,         //数字
            email: /^([0-9A-Za-z\-_\.]+)@([0-9a-z]+\.[a-z]{2,3}(\.[a-z]{2})?)$/,
            phone: /^1[3|4|5|8]\d{9}$/,
            chinese: /^[\u0391-\uFFE5]{2,6}$/, //2-6位中文
            password: /^[a-zA-Z0-9_]{8,32}$/ //6-32位密码验证
        };
        this.validatorArr = {};
    };


    FormValidator.prototype.init = function () {
        var scope = this;
        $('.formValidate').each(function () {
            var el = $(this);
            scope.initItem(el);
        }); //each
    };

    FormValidator.prototype.initItem = function (el, insertType) {
        if (typeof el == 'string') el = $('#' + el);
        var scope = this;
        var cfg = el.attr('data-cfg');
        //临时添加的验证
        if (insertType) {
            cfg.check = true;
        }

        //若是未开启验证便退出该项初始化
        if (cfg.check && cfg.check == false) {
            return false;
        }

        if (cfg && cfg.length > 0) {
            cfg = eval('(' + cfg + ')');
            //            cfg = JSON.parse(cfg);
            var check = cfg.check || true,
            id = el.attr('id') || new Date().getTime(),
            initMsg = cfg.initMsg || '请填入信息',
            sucMsg = cfg.sucMsg || '格式正确',
            errorMsg = cfg.errorMsg || '请注意格式',
            requred = cfg.requred || false,
            msgPosition = cfg.msgPosition || 'right';

            cfg.id = id;
            cfg.check = check;
            cfg.initMsg = initMsg;
            cfg.sucMsg = sucMsg;
            cfg.errorMsg = errorMsg;
            cfg.msgPosition = msgPosition;
            cfg.requred = requred;

            var tips = $('<div class="validateTips validateInit" id="' + id + 'Tips"><div class="triangle_icon"><div class="before"></div><div class="after"></div></div>' + initMsg + '</div>');
            //                var tips = $('<div class="validateTips validateInit" id="' + id + 'Tips">' + initMsg + '</div>');
            var offset = el.offset();
            var height = parseInt(el.outerHeight());
            var width = parseInt(el.outerWidth());
            var l = offset.left;
            var t = offset.top;

            if (msgPosition == 'bottom') {
                tips.addClass('posBottom');
                t += height + 4;
            } else if (msgPosition == 'right') {
                tips.addClass('posRight');
                l += width + 6;
            } else if (msgPosition == 'top') {
                tips.addClass('posTop');
                t += height * (-1) - 8;
            }
            tips.css({ left: l, top: t });
            $('body').append(tips);

            cfg.el = el;
            cfg.tipEl = tips;
            //该表单的验证
            cfg.validate = function () {
                scope.funcValidate(cfg);
            };

            //会触发验证的事件(取消验证后,该事件取消的话害怕引起事件丢失)
            el.change(function () {
                scope.funcValidate(cfg);
            });
            el.focus(function () {
                scope.funcValidate(cfg);
            });

            el.keyup(function () {
                scope.funcValidate(cfg);
            });


            scope.validatorArr[id] = cfg; //生成相关验证对象

        } else {
            console.log('请输入完整验证信息!否则控件会产生错误!');
        }
    };

    FormValidator.prototype.funcValidate = function (cfg) {
        var id = cfg.id;
        var el = cfg.el;
        var check = cfg.check; //判断是否开启验证
        var that = this;

        //取消事件不执行下面逻辑
        if (!this.validatorArr[id])
            return false;
        //若是没有开启验证便忽略之
        if (!check) {
            this.validatorArr[id]['state'] = 'ignore';
            return false;
        }

        var type = cfg.type;
        var regexObj = cfg.regexObj; //正则相关
        var rangeObj = cfg.rangeObj; //范围验证
        var compareObj = cfg.compareObj; //范围验证
        var ajaxObj = cfg.ajaxObj; //ajax验证

        var msg = '';
        var isPass = 1; //0初始化,1成功,-1错误

        //首先进行非空验证
        if (cfg.requred) {
            if (el.val() == '') {
                isPass = -1;
                msg = '该项必填';
            }
        }

        //type优先
        if (isPass == 1 && el.val().length > 0 && typeof type == 'string') {
            var r = this.regexEnum[type];
            //测试通过
            if (r.test(el.val())) {
                msg = cfg.sucMsg;
            } else {
                isPass = -1;
                msg = cfg.errorMsg;
            }
        }

        //当第一步验证通过便执行自身正则验证
        if (isPass == 1 && el.val().length > 0 && regexObj) {
            //当未指定type时候,便执行页面中的正则表达式对象

            var r = regexObj;
            if (r.test(el.val())) {
                msg = cfg.sucMsg;
            } else {
                isPass = -1;
                msg = cfg.errorMsg;
            }
        }

        //当第二步验证通过便执行范围验证
        if (isPass == 1 && el.val().length > 0 && rangeObj) {
            //日期验证暂时忽略
            var rangeArr = rangeObj.split('|');
            if (rangeArr.length == 3) {
                var _v = el.val();
                var _p1 = rangeArr[1];
                var _p2 = rangeArr[2];
                if (rangeArr[0] == 'num') {
                    _v = parseInt(el.val());
                    _p1 = parseInt(rangeArr[1]);
                    _p2 = parseInt(rangeArr[2]);
                }
                if (_v > _p1 && _v < _p2) {
                    msg = cfg.sucMsg;
                } else {
                    isPass = -1;
                    msg = '请填入' + rangeArr[0] + '到' + rangeArr[1] + '直接的数字';
                }
            } else {
                console.log('范围参数错误');
            }
        }

        //执行对比运算
        if (isPass == 1 && el.val().length > 0 && compareObj) {
            var compareArr = compareObj.split('|');
            if (compareArr.length == 3) {
                var _type = compareArr[0]
                var _id = compareArr[1];
                var _flag = compareArr[2];
                var _v = el.val();
                var _cv = $('#' + _id).val();
                if (_type == 'num') {
                    _v = parseInt(_v);
                    _cv = parseInt(_cv);
                }

                if (_flag == '<') {
                    if (_v < _cv) {
                        msg = cfg.sucMsg;
                    } else {
                        isPass = -1;
                        msg = '该值过大';
                    }
                }
                if (_flag == '>') {
                    if (_v > _cv) {
                        msg = cfg.sucMsg;
                    } else {
                        isPass = -1;
                        msg = '该值过小';
                    }
                }
                if (_flag == '=') {
                    if (_v == _cv) {
                        msg = cfg.sucMsg;
                    } else {
                        isPass = -1;
                        msg = '两次数据不一致';
                    }
                }
            } else {
                console.log('范围参数错误');
            }
        }

        //ajax验证
        if (isPass == 1 && el.val().length > 0 && ajaxObj) {
            var ajaxArr = ajaxObj.split('|');
            if (ajaxArr.length == 2) {
                var _url = ajaxArr[0];
                var _param = {};
                _param[ajaxArr[1]] = el.val();
                $.get(_url, _param, function (data) {
                    if (typeof data == 'string')
                        data = eval('(' + data + ')');
                    if (data.retcode == '0') {
                        msg = cfg.sucMsg;
                    } else {
                        isPass = -1;
                        msg = data.msg;
                    }
                    setResult(that, isPass);
                });
                return false; //在此中断后续操作
            }
        }

        setResult(this, isPass);

        function setResult(scope, isPass) {
            if (msg == '') isPass = 0;
            if (isPass == 0) {
                scope.validatorArr[id]['state'] = 'init';
                scope.validatorArr[id]['tipEl'].removeClass('validateError');
                scope.validatorArr[id]['tipEl'].removeClass('validateSuc');
                scope.validatorArr[id]['tipEl'].addClass('validateInit');
                scope.validatorArr[id]['tipEl'].html('<div class="triangle_icon"><div class="before"></div><div class="after"></div></div>' + cfg.initMsg);
            } else if (isPass == 1) {
                scope.validatorArr[id]['state'] = 'success';
                scope.validatorArr[id]['tipEl'].removeClass('validateError');
                scope.validatorArr[id]['tipEl'].removeClass('validateInit');
                scope.validatorArr[id]['tipEl'].addClass('validateSuc');
                scope.validatorArr[id]['tipEl'].html('<div class="triangle_icon"><div class="before"></div><div class="after"></div></div>' + msg);
            } else if (isPass == -1) {
                scope.validatorArr[id]['state'] = 'error';
                scope.validatorArr[id]['tipEl'].removeClass('validateSuc');
                scope.validatorArr[id]['tipEl'].removeClass('validateInit');
                scope.validatorArr[id]['tipEl'].addClass('validateError');
                scope.validatorArr[id]['tipEl'].html('<div class="triangle_icon"><div class="before"></div><div class="after"></div></div>' + msg);
            }
        }
    };

    FormValidator.prototype.validatorAll = function () {
        for (var k in this.validatorArr) {
            var v = this.validatorArr[k];
            v.validate();
        }
    };

    FormValidator.prototype.removeValidator = function (id) {
        if (id && this.validatorArr[id]) {
            this.validatorArr[id].tipEl.remove(); //删除提示信息
            this.validatorArr[id]['check'] = false; //将其验证状态置为false 
            var s = '';
        }
    };

    FormValidator.prototype.addValidator = function (id) {
        var el = $('#' + id);
        this.initItem(el, 'add');
    };

    FormValidator.prototype.validatorState = function () {
        for (var k in this.validatorArr) {
            var v = this.validatorArr[k];
            if (v.state == 'error') {
                return false;
            }
        }
        return true;
    };

    window.FormValidator = FormValidator;

})(jQuery);
View Code

结语

经过2天的奋战,终于完成了,虽然千疮百孔,但是作为练手或者学习的工具还是很不错的,希望对各位朋友有帮助。

若是您发现任何问题,请回复之,我有时间便予以修改,多谢啦!

若是你觉得这篇文章不错请点推荐哦

 

posted on 2013-06-02 00:06  叶小钗  阅读(3692)  评论(21编辑  收藏  举报