表单验证是确保用户输入数据有效性的重要手段,分为客户端验证和服务器端验证。
东巴文(db-w.cn) 认为:良好的表单验证能提升用户体验,减少服务器压力。
客户端验证在浏览器端进行,提供即时反馈:
东巴文验证类型对比:
| 验证类型 | 执行位置 | 优点 | 缺点 |
|---|---|---|---|
| HTML5验证 | 浏览器 | 简单、快速 | 功能有限 |
| JavaScript验证 | 浏览器 | 灵活、强大 | 需编写代码 |
| 服务器验证 | 服务器 | 安全可靠 | 需要网络请求 |
东巴文验证时机:
| 时机 | 事件 | 说明 |
|---|---|---|
| 输入时 | input |
实时验证 |
| 失去焦点 | blur |
离开字段时 |
| 提交前 | submit |
提交表单时 |
| 手动触发 | checkValidity() |
自定义时机 |
required属性指定字段为必填:
<form>
<input type="text" name="username" required>
<input type="email" name="email" required>
<textarea name="message" required></textarea>
<button type="submit">提交</button>
</form>
东巴文说明:
:required伪类设置样式pattern属性使用正则表达式验证:
<form>
<!-- 手机号验证 -->
<input type="tel" name="phone"
pattern="[0-9]{11}"
title="请输入11位手机号"
placeholder="请输入手机号">
<!-- 用户名验证(字母开头,允许字母数字下划线) -->
<input type="text" name="username"
pattern="[a-zA-Z][a-zA-Z0-9_]{5,19}"
title="用户名必须以字母开头,6-20个字符"
placeholder="用户名">
<!-- 邮政编码验证 -->
<input type="text" name="zipcode"
pattern="[0-9]{6}"
title="请输入6位邮政编码"
placeholder="邮政编码">
<!-- 身份证号验证 -->
<input type="text" name="idcard"
pattern="[0-9]{17}[0-9Xx]"
title="请输入18位身份证号"
placeholder="身份证号">
<button type="submit">提交</button>
</form>
东巴文常用正则表达式:
| 验证内容 | 正则表达式 | 说明 |
|---|---|---|
| 手机号 | [0-9]{11} |
11位数字 |
| 邮箱 | [^@]+@[^@]+\.[^@]+ |
简单邮箱格式 |
| 用户名 | [a-zA-Z][a-zA-Z0-9_]{5,19} |
字母开头,6-20字符 |
| 密码 | .{6,20} |
6-20个任意字符 |
| 身份证 | [0-9]{17}[0-9Xx] |
18位身份证号 |
| 邮编 | [0-9]{6} |
6位数字 |
| 网址 | https?://.+ |
http或https开头 |
min和max属性用于数值和日期范围验证:
<form>
<!-- 年龄范围 -->
<input type="number" name="age" min="1" max="120" value="25">
<!-- 日期范围 -->
<input type="date" name="birthday" min="1900-01-01" max="2024-12-31">
<!-- 分数范围 -->
<input type="number" name="score" min="0" max="100" step="0.5">
<button type="submit">提交</button>
</form>
minlength和maxlength属性限制文本长度:
<form>
<!-- 用户名长度 -->
<input type="text" name="username"
minlength="6" maxlength="20"
placeholder="6-20个字符">
<!-- 密码长度 -->
<input type="password" name="password"
minlength="8" maxlength="32"
placeholder="8-32个字符">
<!-- 留言长度 -->
<textarea name="message"
minlength="10" maxlength="500"
placeholder="请输入10-500个字符"></textarea>
<button type="submit">提交</button>
</form>
type属性验证特定类型的输入格式验证:
<form>
<!-- 邮箱验证 -->
<input type="email" name="email" placeholder="请输入邮箱">
<!-- 网址验证 -->
<input type="url" name="website" placeholder="请输入网址">
<!-- 电话验证 -->
<input type="tel" name="phone" pattern="[0-9]{11}" placeholder="请输入手机号">
<button type="submit">提交</button>
</form>
东巴文type验证说明:
| 类型 | 验证规则 | 示例 |
|---|---|---|
email |
必须包含@符号 | test@example.com |
url |
必须是有效URL | https://db-w.cn |
tel |
无默认验证(需pattern) | 13800138000 |
number |
必须是数字 | 123 |
东巴文验证属性列表:
| 属性 | 说明 | 返回值 |
|---|---|---|
willValidate |
是否参与验证 | boolean |
validity |
有效性状态对象 | ValidityState |
validationMessage |
验证错误消息 | string |
checkValidity() |
检查有效性 | boolean |
reportValidity() |
检查并报告错误 | boolean |
setCustomValidity(msg) |
设置自定义错误 | void |
东巴文ValidityState属性:
| 属性 | 说明 | 触发条件 |
|---|---|---|
valueMissing |
值缺失 | required字段为空 |
typeMismatch |
类型不匹配 | email/url格式错误 |
patternMismatch |
模式不匹配 | pattern验证失败 |
tooLong |
超出最大长度 | 超过maxlength |
tooShort |
小于最小长度 | 小于minlength |
rangeUnderflow |
小于最小值 | 小于min |
rangeOverflow |
大于最大值 | 大于max |
stepMismatch |
步长不匹配 | 不符合step |
badInput |
无效输入 | 无法转换为数值 |
customError |
自定义错误 | setCustomValidity设置 |
valid |
是否有效 | 所有验证通过 |
<form id="myForm">
<div class="form-group">
<label for="username">用户名</label>
<input type="text" id="username" name="username"
required minlength="6" maxlength="20"
pattern="[a-zA-Z][a-zA-Z0-9_]{5,19}">
<span class="error" id="username-error"></span>
</div>
<div class="form-group">
<label for="email">邮箱</label>
<input type="email" id="email" name="email" required>
<span class="error" id="email-error"></span>
</div>
<div class="form-group">
<label for="age">年龄</label>
<input type="number" id="age" name="age" min="1" max="120">
<span class="error" id="age-error"></span>
</div>
<button type="submit">提交</button>
</form>
<script>
const form = document.getElementById('myForm');
// 验证用户名
function validateUsername() {
const input = document.getElementById('username');
const error = document.getElementById('username-error');
if (input.validity.valueMissing) {
error.textContent = '请输入用户名';
return false;
} else if (input.validity.tooShort) {
error.textContent = `用户名至少需要${input.minLength}个字符`;
return false;
} else if (input.validity.tooLong) {
error.textContent = `用户名最多${input.maxLength}个字符`;
return false;
} else if (input.validity.patternMismatch) {
error.textContent = '用户名必须以字母开头,只能包含字母、数字、下划线';
return false;
} else {
error.textContent = '';
return true;
}
}
// 验证邮箱
function validateEmail() {
const input = document.getElementById('email');
const error = document.getElementById('email-error');
if (input.validity.valueMissing) {
error.textContent = '请输入邮箱地址';
return false;
} else if (input.validity.typeMismatch) {
error.textContent = '请输入有效的邮箱地址';
return false;
} else {
error.textContent = '';
return true;
}
}
// 验证年龄
function validateAge() {
const input = document.getElementById('age');
const error = document.getElementById('age-error');
if (input.validity.rangeUnderflow) {
error.textContent = `年龄不能小于${input.min}`;
return false;
} else if (input.validity.rangeOverflow) {
error.textContent = `年龄不能大于${input.max}`;
return false;
} else {
error.textContent = '';
return true;
}
}
// 实时验证
document.getElementById('username').addEventListener('input', validateUsername);
document.getElementById('email').addEventListener('input', validateEmail);
document.getElementById('age').addEventListener('input', validateAge);
// 提交验证
form.addEventListener('submit', function(e) {
const isValid = validateUsername() && validateEmail() && validateAge();
if (!isValid) {
e.preventDefault();
}
});
</script>
setCustomValidity()方法设置自定义错误消息:
<form id="myForm">
<div class="form-group">
<label for="password">密码</label>
<input type="password" id="password" name="password" required>
<span class="error" id="password-error"></span>
</div>
<div class="form-group">
<label for="confirm-password">确认密码</label>
<input type="password" id="confirm-password" name="confirm-password" required>
<span class="error" id="confirm-password-error"></span>
</div>
<button type="submit">提交</button>
</form>
<script>
const password = document.getElementById('password');
const confirmPassword = document.getElementById('confirm-password');
confirmPassword.addEventListener('input', function() {
if (this.value !== password.value) {
this.setCustomValidity('两次输入的密码不一致');
} else {
this.setCustomValidity('');
}
});
password.addEventListener('input', function() {
if (confirmPassword.value && confirmPassword.value !== this.value) {
confirmPassword.setCustomValidity('两次输入的密码不一致');
} else {
confirmPassword.setCustomValidity('');
}
});
</script>
东巴文说明:
setCustomValidity(''):清除自定义错误setCustomValidity('消息'):设置自定义错误validity.customError为truenovalidate属性禁用表单验证:
<!-- 禁用整个表单验证 -->
<form novalidate>
<input type="email" name="email" required>
<button type="submit">提交</button>
</form>
formnovalidate属性禁用特定提交按钮的验证:
<form>
<input type="email" name="email" required>
<!-- 正常提交(带验证) -->
<button type="submit">提交</button>
<!-- 保存草稿(不验证) -->
<button type="submit" formnovalidate>保存草稿</button>
</form>
东巴文验证伪类:
| 伪类 | 说明 | 使用场景 |
|---|---|---|
:valid |
验证通过 | 显示成功样式 |
:invalid |
验证失败 | 显示错误样式 |
:required |
必填字段 | 标记必填项 |
:optional |
可选字段 | 标记可选项 |
:in-range |
在范围内 | 数字/日期有效 |
:out-of-range |
超出范围 | 数字/日期无效 |
<style>
/* 必填字段样式 */
input:required {
border-left: 3px solid #f44336;
}
/* 可选字段样式 */
input:optional {
border-left: 3px solid #ccc;
}
/* 验证通过样式 */
input:valid:not(:placeholder-shown) {
border-color: #4CAF50;
}
input:valid:not(:placeholder-shown) + .valid-icon {
display: inline;
}
/* 验证失败样式 */
input:invalid:not(:placeholder-shown) {
border-color: #f44336;
}
input:invalid:not(:placeholder-shown) + .invalid-icon {
display: inline;
}
/* 范围验证样式 */
input[type="number"]:in-range {
border-color: #4CAF50;
}
input[type="number"]:out-of-range {
border-color: #f44336;
}
</style>
<form>
<div class="form-group">
<label for="username">用户名 *</label>
<input type="text" id="username" name="username"
required minlength="6" placeholder="至少6个字符">
<span class="valid-icon" style="display:none; color:green;">✓</span>
<span class="invalid-icon" style="display:none; color:red;">✗</span>
</div>
<div class="form-group">
<label for="email">邮箱 *</label>
<input type="email" id="email" name="email" required placeholder="请输入邮箱">
</div>
<div class="form-group">
<label for="age">年龄</label>
<input type="number" id="age" name="age" min="1" max="120" placeholder="1-120">
</div>
<button type="submit">提交</button>
</form>
<!-- ✅ 推荐:客户端验证提升体验 -->
<input type="email" name="email" required pattern="[^@]+@[^@]+\.[^@]+">
<!-- 服务器端验证确保安全 -->
<!-- 后端代码示例(伪代码) -->
<!--
if (!validateEmail($_POST['email'])) {
return error('邮箱格式错误');
}
-->
<!-- ✅ 推荐:明确的错误消息 -->
<input type="tel" name="phone"
pattern="[0-9]{11}"
title="请输入11位手机号,例如:13800138000">
<!-- ❌ 不推荐:模糊的错误消息 -->
<input type="tel" name="phone" pattern="[0-9]{11}">
<!-- ✅ 推荐:输入时验证 -->
<input type="email" name="email"
oninput="validateEmail(this)"
onblur="validateEmail(this)">
<!-- ❌ 不推荐:只在提交时验证 -->
<form onsubmit="return validateForm()">
<!-- ✅ 推荐:使用HTML5验证属性 -->
<input type="email" name="email" required>
<input type="number" name="age" min="1" max="120">
<!-- ❌ 不推荐:全部使用pattern -->
<input type="text" name="email" pattern="[^@]+@[^@]+\.[^@]+">
<input type="text" name="age" pattern="[0-9]+">
<!-- ✅ 推荐:友好的错误提示 -->
<script>
function validateEmail(input) {
if (input.validity.valueMissing) {
input.setCustomValidity('请输入邮箱地址');
} else if (input.validity.typeMismatch) {
input.setCustomValidity('请输入有效的邮箱地址,例如:test@example.com');
} else {
input.setCustomValidity('');
}
}
</script>
问题1:以下哪个属性用于设置正则表达式验证?
A. regex
B. pattern
C. match
D. validate
答案:B
东巴文解释:pattern属性用于设置正则表达式验证规则。HTML中没有regex、match、validate这些属性。
问题2:以下哪个方法用于设置自定义验证消息?
A. setErrorMessage()
B. setCustomValidity()
C. setValidation()
D. setError()
答案:B
东巴文解释:setCustomValidity()方法用于设置自定义验证消息。HTML5验证API中没有setErrorMessage()、setValidation()、setError()这些方法。
任务:创建一个完整的注册表单,包含以下验证:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>注册表单 - 东巴文</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.container {
background: white;
padding: 40px;
border-radius: 10px;
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
width: 100%;
max-width: 500px;
}
h1 {
text-align: center;
color: #333;
margin-bottom: 30px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
color: #555;
font-weight: 500;
}
label .required {
color: #f44336;
}
input[type="text"],
input[type="email"],
input[type="password"],
input[type="tel"],
input[type="number"] {
width: 100%;
padding: 12px;
border: 2px solid #ddd;
border-radius: 6px;
font-size: 14px;
transition: all 0.3s;
}
input:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
input:valid:not(:placeholder-shown) {
border-color: #4CAF50;
}
input:invalid:not(:placeholder-shown) {
border-color: #f44336;
}
.error-message {
color: #f44336;
font-size: 12px;
margin-top: 5px;
min-height: 18px;
}
.success-message {
color: #4CAF50;
font-size: 12px;
margin-top: 5px;
}
.password-strength {
margin-top: 5px;
height: 4px;
background: #ddd;
border-radius: 2px;
overflow: hidden;
}
.password-strength-bar {
height: 100%;
width: 0;
transition: all 0.3s;
}
.strength-weak { background: #f44336; width: 33%; }
.strength-medium { background: #ff9800; width: 66%; }
.strength-strong { background: #4CAF50; width: 100%; }
button {
width: 100%;
padding: 14px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 6px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: transform 0.3s;
}
button:hover {
transform: translateY(-2px);
}
button:active {
transform: translateY(0);
}
button:disabled {
background: #ccc;
cursor: not-allowed;
transform: none;
}
.success-overlay {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.5);
align-items: center;
justify-content: center;
z-index: 1000;
}
.success-modal {
background: white;
padding: 40px;
border-radius: 10px;
text-align: center;
max-width: 400px;
}
.success-modal h2 {
color: #4CAF50;
margin-bottom: 20px;
}
.success-modal p {
color: #666;
margin-bottom: 20px;
}
</style>
</head>
<body>
<div class="container">
<h1>东巴文注册</h1>
<form id="registerForm">
<div class="form-group">
<label for="username">
用户名 <span class="required">*</span>
</label>
<input type="text" id="username" name="username"
required minlength="6" maxlength="20"
pattern="[a-zA-Z][a-zA-Z0-9_]{5,19}"
placeholder="字母开头,6-20个字符"
autocomplete="username">
<div class="error-message" id="username-error"></div>
</div>
<div class="form-group">
<label for="email">
邮箱 <span class="required">*</span>
</label>
<input type="email" id="email" name="email"
required
placeholder="请输入邮箱地址"
autocomplete="email">
<div class="error-message" id="email-error"></div>
</div>
<div class="form-group">
<label for="phone">
手机号 <span class="required">*</span>
</label>
<input type="tel" id="phone" name="phone"
required pattern="[0-9]{11}"
placeholder="请输入11位手机号"
autocomplete="tel">
<div class="error-message" id="phone-error"></div>
</div>
<div class="form-group">
<label for="password">
密码 <span class="required">*</span>
</label>
<input type="password" id="password" name="password"
required minlength="8" maxlength="32"
placeholder="8-32个字符"
autocomplete="new-password">
<div class="password-strength">
<div class="password-strength-bar" id="password-strength-bar"></div>
</div>
<div class="error-message" id="password-error"></div>
</div>
<div class="form-group">
<label for="confirmPassword">
确认密码 <span class="required">*</span>
</label>
<input type="password" id="confirmPassword" name="confirmPassword"
required
placeholder="请再次输入密码"
autocomplete="new-password">
<div class="error-message" id="confirmPassword-error"></div>
</div>
<div class="form-group">
<label for="age">年龄</label>
<input type="number" id="age" name="age"
min="1" max="120"
placeholder="可选,1-120岁">
<div class="error-message" id="age-error"></div>
</div>
<button type="submit" id="submitBtn">注册</button>
</form>
</div>
<div class="success-overlay" id="successOverlay">
<div class="success-modal">
<h2>✓ 注册成功!</h2>
<p>欢迎加入东巴文学习社区!</p>
<button onclick="closeModal()">确定</button>
</div>
</div>
<script>
const form = document.getElementById('registerForm');
const username = document.getElementById('username');
const email = document.getElementById('email');
const phone = document.getElementById('phone');
const password = document.getElementById('password');
const confirmPassword = document.getElementById('confirmPassword');
const age = document.getElementById('age');
// 验证用户名
function validateUsername() {
const error = document.getElementById('username-error');
if (username.validity.valueMissing) {
error.textContent = '请输入用户名';
return false;
} else if (username.validity.tooShort) {
error.textContent = `用户名至少需要${username.minLength}个字符`;
return false;
} else if (username.validity.tooLong) {
error.textContent = `用户名最多${username.maxLength}个字符`;
return false;
} else if (username.validity.patternMismatch) {
error.textContent = '用户名必须以字母开头,只能包含字母、数字、下划线';
return false;
} else {
error.textContent = '';
return true;
}
}
// 验证邮箱
function validateEmail() {
const error = document.getElementById('email-error');
if (email.validity.valueMissing) {
error.textContent = '请输入邮箱地址';
return false;
} else if (email.validity.typeMismatch) {
error.textContent = '请输入有效的邮箱地址';
return false;
} else {
error.textContent = '';
return true;
}
}
// 验证手机号
function validatePhone() {
const error = document.getElementById('phone-error');
if (phone.validity.valueMissing) {
error.textContent = '请输入手机号';
return false;
} else if (phone.validity.patternMismatch) {
error.textContent = '请输入11位手机号';
return false;
} else {
error.textContent = '';
return true;
}
}
// 验证密码
function validatePassword() {
const error = document.getElementById('password-error');
const strengthBar = document.getElementById('password-strength-bar');
if (password.validity.valueMissing) {
error.textContent = '请输入密码';
strengthBar.className = 'password-strength-bar';
return false;
} else if (password.validity.tooShort) {
error.textContent = `密码至少需要${password.minLength}个字符`;
strengthBar.className = 'password-strength-bar';
return false;
} else if (password.validity.tooLong) {
error.textContent = `密码最多${password.maxLength}个字符`;
strengthBar.className = 'password-strength-bar';
return false;
} else {
error.textContent = '';
// 密码强度检测
const value = password.value;
let strength = 0;
if (value.length >= 8) strength++;
if (/[a-z]/.test(value) && /[A-Z]/.test(value)) strength++;
if (/[0-9]/.test(value)) strength++;
if (/[^a-zA-Z0-9]/.test(value)) strength++;
if (strength <= 1) {
strengthBar.className = 'password-strength-bar strength-weak';
} else if (strength <= 2) {
strengthBar.className = 'password-strength-bar strength-medium';
} else {
strengthBar.className = 'password-strength-bar strength-strong';
}
return true;
}
}
// 验证确认密码
function validateConfirmPassword() {
const error = document.getElementById('confirmPassword-error');
if (confirmPassword.validity.valueMissing) {
error.textContent = '请再次输入密码';
confirmPassword.setCustomValidity('请再次输入密码');
return false;
} else if (confirmPassword.value !== password.value) {
error.textContent = '两次输入的密码不一致';
confirmPassword.setCustomValidity('两次输入的密码不一致');
return false;
} else {
error.textContent = '';
confirmPassword.setCustomValidity('');
return true;
}
}
// 验证年龄
function validateAge() {
const error = document.getElementById('age-error');
if (age.value === '') {
error.textContent = '';
return true;
} else if (age.validity.rangeUnderflow) {
error.textContent = `年龄不能小于${age.min}`;
return false;
} else if (age.validity.rangeOverflow) {
error.textContent = `年龄不能大于${age.max}`;
return false;
} else {
error.textContent = '';
return true;
}
}
// 实时验证
username.addEventListener('input', validateUsername);
email.addEventListener('input', validateEmail);
phone.addEventListener('input', validatePhone);
password.addEventListener('input', () => {
validatePassword();
if (confirmPassword.value) {
validateConfirmPassword();
}
});
confirmPassword.addEventListener('input', validateConfirmPassword);
age.addEventListener('input', validateAge);
// 表单提交
form.addEventListener('submit', function(e) {
e.preventDefault();
const isValid = validateUsername() &&
validateEmail() &&
validatePhone() &&
validatePassword() &&
validateConfirmPassword() &&
validateAge();
if (isValid) {
// 显示成功提示
document.getElementById('successOverlay').style.display = 'flex';
}
});
function closeModal() {
document.getElementById('successOverlay').style.display = 'none';
form.reset();
document.getElementById('password-strength-bar').className = 'password-strength-bar';
}
</script>
</body>
</html>
</details>
东巴文(db-w.cn) - 让编程学习更有趣、更高效!