Search K
Appearance
Appearance
关键词: HTML5表单验证, 内置验证, 自定义验证, 正则表达式, 验证消息, 客户端验证, 服务端验证, 表单安全
HTML5提供了内置的表单验证功能,包括必填字段、数据类型、长度限制等。
<input type="text" required> <!-- 必填 -->
<input type="email" required> <!-- 邮箱格式 -->
<input type="url" required> <!-- URL格式 -->
<input type="number" min="1" max="100"> <!-- 数字范围 -->
<input type="text" minlength="3" maxlength="20"> <!-- 长度限制 -->
<input type="text" pattern="[0-9]{3}-[0-9]{4}"> <!-- 正则表达式 --><!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>HTML5内置验证示例</title>
<style>
.form-container {
max-width: 500px;
margin: 20px auto;
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input, select, textarea {
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 14px;
transition: border-color 0.3s;
}
input:focus, select:focus, textarea:focus {
outline: none;
border-color: #007bff;
box-shadow: 0 0 0 2px rgba(0,123,255,0.25);
}
input:valid {
border-color: #28a745;
}
input:invalid {
border-color: #dc3545;
}
.error-message {
color: #dc3545;
font-size: 12px;
margin-top: 5px;
}
.success-message {
color: #28a745;
font-size: 12px;
margin-top: 5px;
}
.submit-btn {
width: 100%;
padding: 12px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
.submit-btn:hover {
background-color: #0056b3;
}
.validation-summary {
margin-top: 20px;
padding: 15px;
background-color: #f8f9fa;
border-radius: 4px;
border-left: 4px solid #007bff;
}
</style>
</head>
<body>
<div class="form-container">
<h3>HTML5内置验证示例</h3>
<form id="validation-form" novalidate>
<div class="form-group">
<label for="username">用户名(必填,3-20字符):</label>
<input type="text" id="username" name="username"
required minlength="3" maxlength="20"
title="用户名必须是3-20个字符">
<div class="error-message" id="username-error"></div>
</div>
<div class="form-group">
<label for="email">邮箱地址(必填):</label>
<input type="email" id="email" name="email" required
title="请输入有效的邮箱地址">
<div class="error-message" id="email-error"></div>
</div>
<div class="form-group">
<label for="website">个人网站:</label>
<input type="url" id="website" name="website"
title="请输入有效的网址">
<div class="error-message" id="website-error"></div>
</div>
<div class="form-group">
<label for="age">年龄(18-100岁):</label>
<input type="number" id="age" name="age"
min="18" max="100" required
title="年龄必须在18-100岁之间">
<div class="error-message" id="age-error"></div>
</div>
<div class="form-group">
<label for="phone">电话号码(格式:XXX-XXXX-XXXX):</label>
<input type="tel" id="phone" name="phone"
pattern="[0-9]{3}-[0-9]{4}-[0-9]{4}"
title="请输入格式为 XXX-XXXX-XXXX 的电话号码">
<div class="error-message" id="phone-error"></div>
</div>
<div class="form-group">
<label for="password">密码(至少8位,包含数字和字母):</label>
<input type="password" id="password" name="password"
pattern="^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$"
required title="密码必须至少8位,包含数字和字母">
<div class="error-message" id="password-error"></div>
</div>
<button type="submit" class="submit-btn">提交表单</button>
</form>
<div class="validation-summary" id="validation-summary" style="display: none;">
<h4>验证结果:</h4>
<ul id="validation-results"></ul>
</div>
</div>
<script>
const form = document.getElementById('validation-form');
const inputs = form.querySelectorAll('input');
// 为每个输入框添加实时验证
inputs.forEach(input => {
input.addEventListener('input', function() {
validateField(this);
});
input.addEventListener('blur', function() {
validateField(this);
});
});
function validateField(field) {
const errorDiv = document.getElementById(field.id + '-error');
if (field.validity.valid) {
errorDiv.textContent = '';
errorDiv.className = 'success-message';
errorDiv.textContent = '✓ 输入正确';
} else {
errorDiv.className = 'error-message';
errorDiv.textContent = getValidationMessage(field);
}
}
function getValidationMessage(field) {
if (field.validity.valueMissing) {
return '此字段为必填项';
}
if (field.validity.typeMismatch) {
return '请输入正确的格式';
}
if (field.validity.tooShort) {
return `至少需要输入 ${field.minLength} 个字符`;
}
if (field.validity.tooLong) {
return `最多只能输入 ${field.maxLength} 个字符`;
}
if (field.validity.rangeUnderflow) {
return `值不能小于 ${field.min}`;
}
if (field.validity.rangeOverflow) {
return `值不能大于 ${field.max}`;
}
if (field.validity.patternMismatch) {
return field.title || '输入格式不正确';
}
return '输入无效';
}
// 表单提交验证
form.addEventListener('submit', function(e) {
e.preventDefault();
let isValid = true;
const results = [];
inputs.forEach(input => {
if (!input.validity.valid) {
isValid = false;
results.push(`${input.previousElementSibling.textContent}: ${getValidationMessage(input)}`);
} else {
results.push(`${input.previousElementSibling.textContent}: ✓ 验证通过`);
}
});
// 显示验证结果
const summaryDiv = document.getElementById('validation-summary');
const resultsList = document.getElementById('validation-results');
resultsList.innerHTML = results.map(result => `<li>${result}</li>`).join('');
summaryDiv.style.display = 'block';
if (isValid) {
alert('表单验证成功!');
}
});
</script>
</body>
</html>input.setCustomValidity('自定义错误消息');<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>自定义验证消息示例</title>
<style>
.custom-form {
max-width: 500px;
margin: 20px auto;
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input {
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 14px;
}
.validation-message {
margin-top: 5px;
font-size: 12px;
padding: 5px;
border-radius: 3px;
}
.error {
color: #dc3545;
background-color: #f8d7da;
border: 1px solid #f5c6cb;
}
.success {
color: #155724;
background-color: #d4edda;
border: 1px solid #c3e6cb;
}
.password-strength {
height: 5px;
background-color: #e9ecef;
border-radius: 3px;
margin-top: 5px;
overflow: hidden;
}
.strength-bar {
height: 100%;
transition: width 0.3s, background-color 0.3s;
}
.strength-weak { background-color: #dc3545; }
.strength-medium { background-color: #ffc107; }
.strength-strong { background-color: #28a745; }
</style>
</head>
<body>
<div class="custom-form">
<h3>自定义验证消息示例</h3>
<form id="custom-form">
<div class="form-group">
<label for="confirm-password">确认密码:</label>
<input type="password" id="confirm-password" name="confirm-password" required>
<div class="validation-message" id="confirm-password-msg"></div>
</div>
<div class="form-group">
<label for="strong-password">强密码(自定义规则):</label>
<input type="password" id="strong-password" name="strong-password" required>
<div class="password-strength">
<div class="strength-bar" id="strength-bar"></div>
</div>
<div class="validation-message" id="strong-password-msg"></div>
</div>
<div class="form-group">
<label for="unique-username">唯一用户名:</label>
<input type="text" id="unique-username" name="unique-username" required>
<div class="validation-message" id="unique-username-msg"></div>
</div>
<div class="form-group">
<label for="adult-age">成人年龄验证:</label>
<input type="number" id="adult-age" name="adult-age" required>
<div class="validation-message" id="adult-age-msg"></div>
</div>
<button type="submit">提交</button>
</form>
</div>
<script>
const form = document.getElementById('custom-form');
const passwordInput = document.getElementById('strong-password');
const confirmPasswordInput = document.getElementById('confirm-password');
const usernameInput = document.getElementById('unique-username');
const ageInput = document.getElementById('adult-age');
// 已存在的用户名(模拟)
const existingUsernames = ['admin', 'user', 'test', 'demo'];
// 密码强度验证
passwordInput.addEventListener('input', function() {
const password = this.value;
const strength = calculatePasswordStrength(password);
updatePasswordStrength(strength);
validatePassword(password);
});
function calculatePasswordStrength(password) {
let score = 0;
if (password.length >= 8) score += 25;
if (password.length >= 12) score += 25;
if (/[a-z]/.test(password)) score += 15;
if (/[A-Z]/.test(password)) score += 15;
if (/[0-9]/.test(password)) score += 10;
if (/[^A-Za-z0-9]/.test(password)) score += 10;
return Math.min(score, 100);
}
function updatePasswordStrength(strength) {
const strengthBar = document.getElementById('strength-bar');
strengthBar.style.width = strength + '%';
if (strength < 40) {
strengthBar.className = 'strength-bar strength-weak';
} else if (strength < 70) {
strengthBar.className = 'strength-bar strength-medium';
} else {
strengthBar.className = 'strength-bar strength-strong';
}
}
function validatePassword(password) {
const messageDiv = document.getElementById('strong-password-msg');
if (password.length < 8) {
passwordInput.setCustomValidity('密码长度至少8位');
showMessage(messageDiv, '密码长度至少8位', 'error');
} else if (!/[A-Za-z]/.test(password)) {
passwordInput.setCustomValidity('密码必须包含字母');
showMessage(messageDiv, '密码必须包含字母', 'error');
} else if (!/[0-9]/.test(password)) {
passwordInput.setCustomValidity('密码必须包含数字');
showMessage(messageDiv, '密码必须包含数字', 'error');
} else if (!/[^A-Za-z0-9]/.test(password)) {
passwordInput.setCustomValidity('密码必须包含特殊字符');
showMessage(messageDiv, '建议包含特殊字符以提高安全性', 'error');
} else {
passwordInput.setCustomValidity('');
showMessage(messageDiv, '密码强度良好', 'success');
}
}
// 确认密码验证
confirmPasswordInput.addEventListener('input', function() {
const confirmPassword = this.value;
const originalPassword = passwordInput.value;
const messageDiv = document.getElementById('confirm-password-msg');
if (confirmPassword !== originalPassword) {
this.setCustomValidity('两次输入的密码不一致');
showMessage(messageDiv, '两次输入的密码不一致', 'error');
} else {
this.setCustomValidity('');
showMessage(messageDiv, '密码匹配成功', 'success');
}
});
// 用户名唯一性验证
usernameInput.addEventListener('input', function() {
const username = this.value.toLowerCase();
const messageDiv = document.getElementById('unique-username-msg');
if (existingUsernames.includes(username)) {
this.setCustomValidity('用户名已存在');
showMessage(messageDiv, '用户名已存在,请选择其他用户名', 'error');
} else if (username.length < 3) {
this.setCustomValidity('用户名至少3个字符');
showMessage(messageDiv, '用户名至少3个字符', 'error');
} else {
this.setCustomValidity('');
showMessage(messageDiv, '用户名可用', 'success');
}
});
// 年龄验证
ageInput.addEventListener('input', function() {
const age = parseInt(this.value);
const messageDiv = document.getElementById('adult-age-msg');
if (age < 18) {
this.setCustomValidity('必须年满18岁');
showMessage(messageDiv, '必须年满18岁才能注册', 'error');
} else if (age > 120) {
this.setCustomValidity('年龄不能超过120岁');
showMessage(messageDiv, '请输入真实年龄', 'error');
} else {
this.setCustomValidity('');
showMessage(messageDiv, '年龄验证通过', 'success');
}
});
function showMessage(element, message, type) {
element.textContent = message;
element.className = `validation-message ${type}`;
}
// 表单提交
form.addEventListener('submit', function(e) {
e.preventDefault();
const isValid = form.checkValidity();
if (isValid) {
alert('表单验证成功!');
} else {
alert('请检查表单中的错误');
}
});
</script>
</body>
</html>// 手机号码(中国大陆)
const phoneRegex = /^1[3-9]\d{9}$/;
// 身份证号码(中国大陆)
const idCardRegex = /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}(\d|X)$/;
// 邮政编码
const postalCodeRegex = /^\d{6}$/;
// 银行卡号
const bankCardRegex = /^\d{16,19}$/;<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>正则表达式验证示例</title>
<style>
.regex-form {
max-width: 600px;
margin: 20px auto;
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
input {
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 14px;
}
.validation-result {
margin-top: 8px;
padding: 8px;
border-radius: 4px;
font-size: 12px;
}
.valid {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.invalid {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.pattern-info {
font-size: 12px;
color: #666;
margin-top: 5px;
}
</style>
</head>
<body>
<div class="regex-form">
<h3>正则表达式验证示例</h3>
<form>
<div class="form-group">
<label for="phone">手机号码:</label>
<input type="text" id="phone" placeholder="请输入11位手机号">
<div class="pattern-info">格式:1开头的11位数字</div>
<div class="validation-result" id="phone-result"></div>
</div>
<div class="form-group">
<label for="id-card">身份证号码:</label>
<input type="text" id="id-card" placeholder="请输入18位身份证号">
<div class="pattern-info">格式:18位数字,最后一位可以是X</div>
<div class="validation-result" id="id-card-result"></div>
</div>
<div class="form-group">
<label for="postal-code">邮政编码:</label>
<input type="text" id="postal-code" placeholder="请输入6位邮政编码">
<div class="pattern-info">格式:6位数字</div>
<div class="validation-result" id="postal-code-result"></div>
</div>
<div class="form-group">
<label for="bank-card">银行卡号:</label>
<input type="text" id="bank-card" placeholder="请输入银行卡号">
<div class="pattern-info">格式:16-19位数字</div>
<div class="validation-result" id="bank-card-result"></div>
</div>
<div class="form-group">
<label for="qq-number">QQ号码:</label>
<input type="text" id="qq-number" placeholder="请输入QQ号">
<div class="pattern-info">格式:5-11位数字,不能以0开头</div>
<div class="validation-result" id="qq-number-result"></div>
</div>
<div class="form-group">
<label for="license-plate">车牌号码:</label>
<input type="text" id="license-plate" placeholder="请输入车牌号">
<div class="pattern-info">格式:省份简称+字母+5位数字或字母</div>
<div class="validation-result" id="license-plate-result"></div>
</div>
</form>
</div>
<script>
// 正则表达式定义
const patterns = {
phone: {
regex: /^1[3-9]\d{9}$/,
message: '手机号码格式正确',
errorMessage: '请输入有效的手机号码'
},
idCard: {
regex: /^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}(\d|X)$/i,
message: '身份证号码格式正确',
errorMessage: '请输入有效的身份证号码'
},
postalCode: {
regex: /^\d{6}$/,
message: '邮政编码格式正确',
errorMessage: '请输入6位数字的邮政编码'
},
bankCard: {
regex: /^\d{16,19}$/,
message: '银行卡号格式正确',
errorMessage: '请输入16-19位数字的银行卡号'
},
qqNumber: {
regex: /^[1-9]\d{4,10}$/,
message: 'QQ号码格式正确',
errorMessage: '请输入5-11位数字的QQ号码'
},
licensePlate: {
regex: /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-Z0-9]{4}[A-Z0-9挂学警港澳]{1}$/,
message: '车牌号码格式正确',
errorMessage: '请输入有效的车牌号码'
}
};
// 为每个输入框添加验证
Object.keys(patterns).forEach(key => {
const input = document.getElementById(key.replace(/([A-Z])/g, '-$1').toLowerCase());
const result = document.getElementById(key.replace(/([A-Z])/g, '-$1').toLowerCase() + '-result');
input.addEventListener('input', function() {
validateWithRegex(this, result, patterns[key]);
});
});
function validateWithRegex(input, resultDiv, pattern) {
const value = input.value.trim();
if (value === '') {
resultDiv.textContent = '';
resultDiv.className = 'validation-result';
return;
}
if (pattern.regex.test(value)) {
resultDiv.textContent = '✓ ' + pattern.message;
resultDiv.className = 'validation-result valid';
} else {
resultDiv.textContent = '✗ ' + pattern.errorMessage;
resultDiv.className = 'validation-result invalid';
}
}
// 银行卡号格式化显示
document.getElementById('bank-card').addEventListener('input', function() {
let value = this.value.replace(/\s/g, '');
let formatted = value.replace(/(\d{4})(?=\d)/g, '$1 ');
this.value = formatted;
});
// 身份证号码校验码验证
function validateIdCard(idCard) {
const weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
const checkCodes = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
let sum = 0;
for (let i = 0; i < 17; i++) {
sum += parseInt(idCard.charAt(i)) * weights[i];
}
const checkCode = checkCodes[sum % 11];
return checkCode === idCard.charAt(17).toUpperCase();
}
// 增强身份证验证
document.getElementById('id-card').addEventListener('input', function() {
const value = this.value.trim();
const result = document.getElementById('id-card-result');
if (value === '') {
result.textContent = '';
result.className = 'validation-result';
return;
}
if (patterns.idCard.regex.test(value)) {
if (validateIdCard(value)) {
result.textContent = '✓ 身份证号码格式正确,校验码验证通过';
result.className = 'validation-result valid';
} else {
result.textContent = '✗ 身份证号码校验码错误';
result.className = 'validation-result invalid';
}
} else {
result.textContent = '✗ 请输入有效的身份证号码';
result.className = 'validation-result invalid';
}
});
</script>
</body>
</html>// 实时验证
input.addEventListener('input', function() {
validateField(this);
});
// 提交验证
form.addEventListener('submit', function(e) {
if (!validateForm()) {
e.preventDefault();
}
});const errorMessages = {
required: '此字段为必填项',
email: '请输入有效的邮箱地址',
minLength: '输入内容过短',
maxLength: '输入内容过长',
pattern: '输入格式不正确'
};.input-valid {
border-color: #28a745;
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8"><path fill="%2328a745" d="M2.3 6.73L.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z"/></svg>');
background-repeat: no-repeat;
background-position: right 10px center;
background-size: 16px 16px;
}
.input-invalid {
border-color: #dc3545;
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8 8"><path fill="%23dc3545" d="M7.5 6.5L5 4 7.5 1.5 6.5 0.5 4 3 1.5 0.5 0.5 1.5 3 4 0.5 6.5 1.5 7.5 4 5 6.5 7.5z"/></svg>');
background-repeat: no-repeat;
background-position: right 10px center;
background-size: 16px 16px;
}A: HTML5验证是浏览器原生功能,JavaScript验证更灵活,可以提供更丰富的用户体验。
A: 使用渐进增强策略,提供JavaScript回退方案,确保基本验证功能正常工作。
A: 不够,客户端验证主要用于提升用户体验,服务器端验证是安全的关键。
A: 建议在用户完成输入后(失焦时)进行验证,避免在输入过程中频繁提示错误。
下一节预览:第4章第1节 - 音频元素 - 学习HTML5音频元素的使用