正则驱动实时表单验证(HTML+CSS+JS+正则)

上图!!!

HTML源码

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>正则驱动实时表单验证</title>
    <link rel="stylesheet" href="./static/css/正则驱动实时表单验证.css">
</head>

<body>
    <form id="regForm" onsubmit="return false">
        <div class="form-group">
            <label for="username">用户名</label>
            <div class="input-wrap">
                <input type="text" id="username" placeholder="3-16位字母/数字/下划线">
                <span class="icon" id="usernameIcon"></span>
            </div>
            <span class="error-msg" id="usernameErr"></span>
        </div>

        <div class="form-group">
            <label for="password">密码</label>
            <div class="input-wrap">
                <input type="password" id="password" placeholder="8-20位,含大/小/数/特殊字符">
                <span class="icon" id="passwordIcon"></span>
            </div>
            <div class="strength">
                <div class="strength-bar"><span id="strengthFill"></span></div>
                <span id="strengthText"></span>
            </div>
            <span class="error-msg" id="passwordErr"></span>
        </div>

        <div class="form-group">
            <label for="confirm">确认密码</label>
            <div class="input-wrap">
                <input type="password" id="confirm" placeholder="再次输入密码">
                <span class="icon" id="confirmIcon"></span>
            </div>
            <span class="error-msg" id="confirmErr"></span>
        </div>

        <div class="form-group">
            <label for="phone">手机号</label>
            <div class="input-wrap">
                <input type="text" id="phone" placeholder="11位手机号">
                <span class="icon" id="phoneIcon"></span>
            </div>
            <span class="error-msg" id="phoneErr"></span>
        </div>

        <div class="form-group">
            <label for="email">邮箱</label>
            <div class="input-wrap">
                <input type="text" id="email" placeholder="包含@且@后至少一个字符">
                <span class="icon" id="emailIcon"></span>
            </div>
            <span class="error-msg" id="emailErr"></span>
        </div>

        <button id="submitBtn" disabled>注册</button>
    </form>

    <script>
        // 各字段状态,用于提交按钮联动
        const state = { username: false, password: false, confirm: false, phone: false, email: false };

        // 设置单个字段的通过/失败展示(动态改 class + 插入提示)
        function setResult(field, ok, msg) {
            const input = document.getElementById(field);
            const icon = document.getElementById(field + 'Icon');
            const err = document.getElementById(field + 'Err');
            state[field] = ok;
            input.classList.toggle('valid', ok);
            input.classList.toggle('invalid', !ok);
            icon.textContent = ok ? '✓' : '✗';
            icon.classList.toggle('valid', ok);
            icon.classList.toggle('invalid', !ok);
            err.textContent = ok ? '' : msg;
            checkAll();
        }

        // 全部通过才启用提交按钮
        function checkAll() {
            const allOk = Object.values(state).every(Boolean);
            document.getElementById('submitBtn').disabled = !allOk;
        }

        // 正则定义
        const reUser = /^[a-zA-Z0-9_]{3,16}$/;
        const rePhone = /^1[3-9]\d{9}$/;
        const reEmail = /^[^@\s]+@[^@\s]+$/;

        // 密码四条小规则(用于强度判定)
        const pwRules = [
            /[a-z]/,      // 小写
            /[A-Z]/,      // 大写
            /\d/,         // 数字
            /[^a-zA-Z0-9]/,    // 特殊字符
        ];

        function validatePassword(val) {
            const lenOk = val.length >= 8 && val.length <= 20;
            const allKinds = pwRules.every(r => r.test(val));
            return lenOk && allKinds;
        }

        // 强度条:按命中种类数 + 长度判定 弱/中/强
        function updateStrength(val) {
            const fill = document.getElementById('strengthFill');
            const text = document.getElementById('strengthText');
            if (!val) { fill.style.width = '0'; text.textContent = ''; return; }
            const score = pwRules.filter(r => r.test(val)).length;
            const lenOk = val.length >= 8 && val.length <= 20;
            let level = '弱', color = '#e74c3c', pct = '33%';
            if (score >= 4 && lenOk) { level = '强'; color = '#2ecc71'; pct = '100%'; }
            else if (score >= 3) { level = '中'; color = '#f39c12'; pct = '66%'; }
            fill.style.width = pct;
            fill.style.background = color;
            text.textContent = '强度:' + level;
            text.style.color = color;
        }

        // 绑定事件
        const $ = id => document.getElementById(id);

        $('username').addEventListener('blur', e => {
            const v = e.target.value.trim();
            setResult('username', reUser.test(v), '用户名需3-16位字母/数字/下划线');
        });

        $('password').addEventListener('input', e => {
            const v = e.target.value;
            updateStrength(v);                       // 实时强度条
            setResult('password', validatePassword(v), '密码需8-20位且含大/小/数/特殊字符');
        });
        $('password').addEventListener('blur', e => {
            const v = e.target.value;
            setResult('password', validatePassword(v), '密码需8-20位且含大/小/数/特殊字符');
            validateConfirm($('confirm').value);     // 密码失焦联动验证确认密码
        });

        function validateConfirm(v) {
            const pw = $('password').value;
            setResult('confirm', v !== '' && v === pw, '两次输入的密码不一致');
        }
        $('confirm').addEventListener('input', e => validateConfirm(e.target.value));

        $('phone').addEventListener('blur', e => {
            const v = e.target.value.trim();
            setResult('phone', rePhone.test(v), '请输入正确的11位手机号');
        });

        $('email').addEventListener('blur', e => {
            const v = e.target.value.trim();
            setResult('email', reEmail.test(v), '邮箱格式不正确(需含@且@后至少一个字符)');
        });
    </script>
</body>

</html>

CSS源码

css 复制代码
* {
    box-sizing: border-box;
}

body {
    font-family: system-ui, sans-serif;
    max-width: 420px;
    margin: 40px auto;
}

.form-group {
    margin-bottom: 18px;
}

label {
    display: block;
    margin-bottom: 6px;
    font-weight: 600;
}

.input-wrap {
    position: relative;
    display: flex;
    align-items: center;
}

input {
    width: 100%;
    padding: 8px 30px 8px 10px;
    border: 1px solid #ccc;
    border-radius: 4px;
    outline: none;
}

.icon {
    position: absolute;
    right: 10px;
    font-weight: bold;
}

/* 验证效果:通过绿、失败红 */
input.valid {
    border-color: #2ecc71;
}

input.invalid {
    border-color: #e74c3c;
}

.icon.valid {
    color: #2ecc71;
}

.icon.invalid {
    color: #e74c3c;
}

.error-msg {
    display: block;
    min-height: 16px;
    color: #e74c3c;
    font-size: 12px;
    margin-top: 4px;
}

.strength {
    margin-top: 6px;
}

.strength-bar {
    height: 6px;
    background: #eee;
    border-radius: 3px;
    overflow: hidden;
}

#strengthFill {
    display: block;
    height: 100%;
    width: 0;
    transition: width .2s, background .2s;
}

#strengthText {
    font-size: 12px;
}

button {
    padding: 10px 20px;
    border: none;
    border-radius: 4px;
    background: #3498db;
    color: #fff;
    cursor: pointer;
}

button:disabled {
    background: #bdc3c7;
    cursor: not-allowed;
}
相关推荐
新中地GIS开发老师1 小时前
零基础WebGIS开发入门 | GeoJSON数据持久化
前端·javascript·gis·webgis·三维gis开发
Quz2 小时前
QML 与 JavaScript 交互方式:内联函数、外部文件、信号槽与工作线程
javascript·qt
Allshadow3 小时前
Nest.js - 连接数据库
javascript·nestjs
冰暮流星3 小时前
javascript之date对象
开发语言·javascript·ecmascript
橘子星3 小时前
别光看教程!手把手拆解 React Todo 项目,一文吃透 5 个核心概念
前端·javascript
大白要努力!3 小时前
纯前端实现 PDF 加水印工具 —— 零后端、支持中文、实时预览
前端·pdf·html
蜡台3 小时前
使用 uni-popup 实现数据选择器Data-Picker
前端·javascript·html·uniapp·uni-popup·data-picker
blns_yxl3 小时前
Promise封装Fetch + 重试机制(HTML+JS)
前端·javascript·html