各位加了一天班累了吧?那我們來繼續未完的表單驗證吧


前言

接着昨天的寫:工作了一個星期各位一定累了吧,那我們一起來表單驗證一番吧!

因為昨天寫到后面確實寫不動了,就停下了,今天我們繼續吧,看看能不能解決昨天提出的幾個問題,由於今天看了下別人寫的插件,在里面找到了一些很不錯的點子,這里也應用起來了,慢慢改造我們的程序吧。

現狀

經過今天的奮戰,我們的界面變成了這個樣子啦:

代碼變成了這個樣子:

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天的奮戰,終於完成了,雖然千瘡百孔,但是作為練手或者學習的工具還是很不錯的,希望對各位朋友有幫助。

若是您發現任何問題,請回復之,我有時間便予以修改,多謝啦!

若是你覺得這篇文章不錯請點推薦哦

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM