JS 基礎篇(策略模式-表單驗證案例)

2b勿擾發表於2020-01-20
<!DOCTYPE html>

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title></title>
    <meta name="description" content="">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="">
    <style>
        ul {
            margin: 0;
            padding: 0;
            list-style: none;
        }

        .fn-textcenter {
            text-align: center;
        }

        .main {
            max-width: 1000px;
            margin: 0 auto;
            zoom: 1;
        }

        .main .top {
            text-align: center;
        }

        .list ul li {
            padding: 10px;
        }

        .list ul li label {
            width: 40%;
            display: inline-block;
            padding: 0 10px;
            text-align: right;
            vertical-align: middle;
        }

        .list ul li input {
            border: 1px solid #e2e2e2;
            padding: 10px;
            width: 200px;
        }

        .list ul li input.valid {
            border: 1px solid green;
        }

        .list ul li input.error {
            border: 1px solid red;
        }

        .list ul li .btn-blue {
            background: #06a5e1;
            color: #fff;
            text-align: center;
            display: inline-block;
            padding: 5px 30px;
            cursor: pointer;
            border-radius: 5px;
            -webkit-border-radius: 5px;
            -moz-border-radius: 5px;
            -o-border-radius: 5px;
        }

        .list ul li .btn-blue:disabled {
            background: #999;
            cursor: not-allowed;
        }

        .list ul li .btn-blue:not([disabled]):hover {
            opacity: 0.8;
            filter: alpha(opacity=80);
            /* 針對 IE8 以及更早的版本 */
        }

        .list input:focus {
            border-color: #66AFE9;
            box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075) inset, 0 0 8px rgba(102, 175, 233, 0.6);
            outline: 0 none;
        }

        .list .tip-error {
            background-color: #F2DEDE;
            border-color: #EED3D7;
            color: #B94A48;
            vertical-align: middle;
            padding: 5px 30px;
            display: none;
        }

        .error-msg {
            color: red;
            font-size: 12px;
        }
    </style>
</head>

<body>
    <div class="main">
        <div class="top">
            <div class="">
                <h2>註冊</h2>
            </div>
        </div>

        <!-- 使用者名稱不能為空。
     密碼長度不能少於 6 位。 
     手機號碼必須符合格式 -->
        <form id="registerForm">
            <div class="list">
                <ul>
                    <li>
                        <label>使用者名稱</label>
                        <input type="text" name="userName" class="name" placeholder="輸入您的使用者名稱">
                    </li>
                    <li>
                        <label>密碼</label>
                        <input type="password" name="password" class="pwd" placeholder="輸入您的密碼">
                    </li>
                    <li>
                        <label>手機</label>
                        <input type="text" name="phoneNumber" class="mobile" placeholder="輸入您的手機">
                    </li>
                    <li class="fn-textcenter"><button type="button" id="btnSubmit"
                            class="btn-blue btn-submit">提交</button></li>
                </ul>
            </div>
    </div>
    </form>

    <script>
        var checkResult = {
            errorTip: function (dom, errMsg) {
                var errDom;
                //先判斷li後面有沒有error-msg這dom 
                var errmsgT = dom.parentElement.querySelector('.error-msg');
                if (!errmsgT) {
                    errDom = document.createElement('span');
                    errDom.className = 'error-msg';
                    errDom.innerText = errMsg;
                    dom.parentElement.appendChild(errDom);
                }
            },
            ok: function (dom) {
                //移除所有的提示,並把input裡紅色部分去掉,加上正確的提示  
                dom.classList.remove('error');
                dom.classList.add('valid');
                var errmsgT = dom.parentElement.querySelector('.error-msg');
                if (errmsgT) {
                    dom.parentElement.querySelector('.error-msg').remove();
                }
            },
            no: function (dom, errMsg) {
                dom.classList.remove('valid');
                dom.classList.add('error');
                this.errorTip(dom, errMsg);
            }
        }

        // 規則集
        // 用策略模式重構表單校驗
        //  strategies['minLength']('ss',6,'error')
        var strategies = {
            minLength: function (errMsg, length) {
                if (this.value.length < length) {
                    checkResult.no(this, errMsg);
                    return errMsg;
                } else {
                    checkResult.ok(this);
                }
            },
            isNumber: function (errMsg) {
                if (!/\d+/.test(this.value)) {
                    checkResult.no(this, errMsg);
                    return errMsg;
                } else {
                    checkResult.ok(this);
                }
            },
            required: function (errMsg) {
                if (this.value === '') {
                    checkResult.no(this, errMsg);
                    return errMsg;
                } else {
                    checkResult.ok(this);
                }
            },
            isMobile: function (errMsg) {
                if (!/(^1[3|5|8][0-9]{9}$)/.test(this.value)) {
                    checkResult.no(this, errMsg);
                    return errMsg;
                } else {
                    checkResult.ok(this);
                }
            }
        };

        // 校驗器
        function Validator() {
            this.items = [];
        };

        Validator.prototype = {
            constructor: Validator,
            // 新增校驗規則
            add: function (dom, rule) {
                for (let i = 0, len = rule.length; i < len; i++) {
                    var strategy = rule[i].strategy;
                    var errorMsg = rule[i].errorMsg;

                    //特殊處理 
                    if (strategy.indexOf('minLength') !== -1) {
                        var temp = strategy.split(':');
                        var minLen = temp[1]
                        strategy = temp[0];
                    }
                    console.log(strategy);
                    this.items.push(strategies[strategy].bind(dom, errorMsg, minLen));
                }
            },

            // 開始校驗
            start: function () {
                for (var i = 0; i < this.items.length; ++i) {
                    var ret = this.items[i]();
                    if (ret) {
                        console.log('oooo');
                        // break;        
                    }
                }
            }
        }

        var validate = new Validator();
        var registerForm = document.getElementById('registerForm');
        validate.add(registerForm.userName, [{
            strategy: 'required',
            errorMsg: '使用者名稱不能為空'
        }, {
            strategy: 'isNumber',
            errorMsg: '只能為數字'
        }]);
        validate.add(registerForm.password, [{
            strategy: 'required',
            errorMsg: '密碼不能為空'
        }]);
        validate.add(registerForm.phoneNumber, [{
            strategy: 'isMobile',
            errorMsg: '請輸入正確的手機號'
        }]);

        document.getElementById('btnSubmit').onclick = function () {
            var ret = validate.start();
            console.log(ret);
        }
        registerForm.querySelectorAll('input').forEach(function (option, index) {
            option.addEventListener('blur', function (ev) {
                var ret = validate.start();
            })
        })
    </script>
</body>

</html>

相關文章