一、HTML5 表单概述
HTML5 为表单开发带来了革命性改进,新增了多种输入类型、属性和验证机制,显著提升了用户体验和开发效率。相比传统表单,HTML5 表单具有以下优势:
- 内置验证:减少 JavaScript 验证代码量
- 移动端优化:自动适配键盘类型
- 语义化元素:更清晰的代码结构
- 丰富的 UI 控件:日期选择器、颜色选择器等
二、基础表单结构
1. 标准表单模板
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>
<style>
body { font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; }
.form-group { margin-bottom: 15px; }
label { display: block; margin-bottom: 5px; font-weight: bold; }
input, select, textarea {
width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px;
box-sizing: border-box;
}
button {
background-color: #4CAF50; color: white; padding: 10px 15px;
border: none; border-radius: 4px; cursor: pointer;
}
button:hover { background-color: #45a049; }
</style>
</head>
<body>
<h1>用户注册</h1>
<form id="registerForm" action="/submit" method="POST">
<!-- 表单元素将放在这里 -->
<div class="form-group">
<button type="submit">提交</button>
</div>
</form>
</body>
</html>
2. 表单基础属性
html<form
action="/api/submit"
method="POST"
enctype="multipart/form-data"
target="_blank"
autocomplete="on"
novalidate
>
<!-- 表单内容 -->
</form>
action
: 表单提交的URLmethod
: HTTP方法(GET/POST)enctype
: 编码类型(文件上传需用multipart/form-data
)target
: 响应打开方式(如_blank
新窗口)autocomplete
: 自动完成功能novalidate
: 禁用浏览器验证
三、HTML5 新增输入类型
1. 文本类输入
html<div class="form-group">
<label for="username">用户名</label>
<input type="text" id="username" name="username"
placeholder="4-16个字符"
minlength="4"
maxlength="16"
required>
</div>
<div class="form-group">
<label for="email">电子邮箱</label>
<input type="email" id="email" name="email"
multiple <!-- 允许输入多个邮箱,用逗号分隔 -->
pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$">
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" id="password" name="password"
minlength="8"
required
pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}"
title="必须包含大小写字母和数字">
</div>
<div class="form-group">
<label for="search">搜索</label>
<input type="search" id="search" name="search"
results="5" <!-- 已废弃,建议使用datalist -->
placeholder="输入关键词...">
</div>
2. 数字与范围输入
html<div class="form-group">
<label for="age">年龄</label>
<input type="number" id="age" name="age"
min="18"
max="99"
step="1"
value="18">
</div>
<div class="form-group">
<label for="price">价格范围</label>
<input type="range" id="price" name="price"
min="0"
max="1000"
step="10"
value="500"
oninput="rangeValue.value = this.value">
<output id="rangeValue">500</output> 元
</div>
3. 日期时间输入
html<div class="form-group">
<label for="birthday">生日</label>
<input type="date" id="birthday" name="birthday"
min="1900-01-01"
max="2023-12-31">
</div>
<div class="form-group">
<label for="appt">预约时间</label>
<input type="datetime-local" id="appt" name="appt"
min="2023-01-01T09:00"
max="2023-12-31T18:00">
</div>
<div class="form-group">
<label for="meeting">会议时间</label>
<input type="time" id="meeting" name="meeting"
min="09:00"
max="17:00"
step="900"> <!-- 15分钟间隔 -->
</div>
<div class="form-group">
<label for="week">选择周数</label>
<input type="week" id="week" name="week">
</div>
<div class="form-group">
<label for="month">选择月份</label>
<input type="month" id="month" name="month">
</div>
4. 特殊输入类型
html<div class="form-group">
<label for="url">个人网站</label>
<input type="url" id="url" name="url"
placeholder="https://example.com">
</div>
<div class="form-group">
<label for="tel">电话号码</label>
<input type="tel" id="tel" name="tel"
pattern="[0-9]{3}-[0-9]{8}"
placeholder="格式:010-12345678">
</div>
<div class="form-group">
<label for="color">选择颜色</label>
<input type="color" id="color" name="color"
value="#ff0000">
</div>
四、表单控件与元素
1. 选择类控件
html<div class="form-group">
<label for="country">国家/地区</label>
<select id="country" name="country" required>
<option value="">请选择...</option>
<optgroup label="亚洲">
<option value="CN">中国</option>
<option value="JP">日本</option>
<option value="KR">韩国</option>
</optgroup>
<optgroup label="欧洲">
<option value="UK">英国</option>
<option value="FR">法国</option>
</optgroup>
</select>
</div>
<div class="form-group">
<label>兴趣爱好(多选)</label>
<select name="interests" multiple size="4">
<option value="sports">运动</option>
<option value="music">音乐</option>
<option value="reading">阅读</option>
<option value="travel">旅行</option>
</select>
</div>
<div class="form-group">
<label>订阅选项</label>
<div>
<input type="checkbox" id="subscribe1" name="subscribe" value="newsletter">
<label for="subscribe1" style="display: inline;">新闻邮件</label>
</div>
<div>
<input type="checkbox" id="subscribe2" name="subscribe" value="promotion" checked>
<label for="subscribe2" style="display: inline;">促销信息</label>
</div>
</div>
<div class="form-group">
<label>性别</label>
<div>
<input type="radio" id="male" name="gender" value="male" checked>
<label for="male" style="display: inline;">男</label>
</div>
<div>
<input type="radio" id="female" name="gender" value="female">
<label for="female" style="display: inline;">女</label>
</div>
<div>
<input type="radio" id="other" name="gender" value="other">
<label for="other" style="display: inline;">其他</label>
</div>
</div>
2. 文件上传
html<div class="form-group">
<label for="avatar">上传头像</label>
<input type="file" id="avatar" name="avatar"
accept="image/*"
multiple>
</div>
<div class="form-group">
<label for="resume">上传简历</label>
<input type="file" id="resume" name="resume"
accept=".pdf,.doc,.docx,application/msword">
</div>
3. 文本域与计量器
html<div class="form-group">
<label for="bio">个人简介</label>
<textarea id="bio" name="bio"
rows="5"
maxlength="500"
placeholder="介绍一下你自己..."></textarea>
<div style="text-align: right;">
<small><span id="charCount">0</span>/500</small>
</div>
</div>
<div class="form-group">
<label for="storage">存储空间使用情况</label>
<meter id="storage" value="65" min="0" max="100" low="30" high="80" optimum="50">
65%
</meter>
</div>
<div class="form-group">
<label for="confidence">信心指数</label>
<progress id="confidence" value="70" max="100">70%</progress>
</div>
五、HTML5 表单验证
1. 内置验证属性
html<div class="form-group">
<label for="username">用户名</label>
<input type="text" id="username" name="username"
required
minlength="4"
maxlength="16"
pattern="[A-Za-z0-9_]+"
title="只能包含字母、数字和下划线"
oninvalid="setCustomValidity('请输入4-16位字母、数字或下划线')"
oninput="setCustomValidity('')">
</div>
<div class="form-group">
<label for="zip">邮政编码</label>
<input type="text" id="zip" name="zip"
pattern="\d{6}"
title="6位数字邮政编码">
</div>
2. JavaScript 验证
html<script>
document.getElementById('registerForm').addEventListener('submit', function(e) {
const password = document.getElementById('password').value;
const confirmPassword = document.getElementById('confirmPassword').value;
if (password !== confirmPassword) {
alert('两次输入的密码不一致!');
e.preventDefault(); // 阻止表单提交
}
// 更复杂的验证可以结合正则表达式
const phone = document.getElementById('tel').value;
const phoneRegex = /^1[3-9]\d{9}$/;
if (!phoneRegex.test(phone)) {
alert('请输入有效的手机号码');
e.preventDefault();
}
});
// 实时验证示例
document.getElementById('email').addEventListener('input', function() {
const email = this.value;
const feedback = document.getElementById('emailFeedback');
if (!email.includes('@')) {
feedback.textContent = '邮箱地址必须包含@符号';
feedback.style.color = 'red';
} else {
feedback.textContent = '邮箱格式正确';
feedback.style.color = 'green';
}
});
</script>
3. 验证样式反馈
css/* 自定义验证样式 */
input:valid {
border-color: #4CAF50;
background-color: #f9fff9;
}
input:invalid {
border-color: #f44336;
background-color: #fff9f9;
}
input:required:placeholder-shown {
border-color: #ff9800;
}
/* 工具提示样式 */
input + .tooltip {
visibility: hidden;
background-color: #555;
color: #fff;
padding: 5px 10px;
border-radius: 4px;
position: absolute;
z-index: 1;
}
input:invalid + .tooltip {
visibility: visible;
}
六、高级表单技术
1. 数据列表(自动完成)
html<div class="form-group">
<label for="browser">选择浏览器</label>
<input list="browsers" id="browser" name="browser">
<datalist id="browsers">
<option value="Chrome">
<option value="Firefox">
<option value="Safari">
<option value="Edge">
<option value="Opera">
</datalist>
</div>
<div class="form-group">
<label for="city">城市</label>
<input list="cities" id="city" name="city">
<datalist id="cities">
<option value="北京">
<option value="上海">
<option value="广州">
<option value="深圳">
<option value="杭州">
</datalist>
</div>
2. 表单字段集
html<fieldset>
<legend>联系方式</legend>
<div class="form-group">
<label for="phone">电话</label>
<input type="tel" id="phone" name="phone">
</div>
<div class="form-group">
<label for="emergency-phone">紧急联系人电话</label>
<input type="tel" id="emergency-phone" name="emergency_phone">
</div>
</fieldset>
<fieldset disabled>
<legend>管理员设置(禁用)</legend>
<div class="form-group">
<label for="role">角色</label>
<select id="role" name="role">
<option value="admin">管理员</option>
<option value="editor">编辑</option>
</select>
</div>
</fieldset>
3. 表单按钮
html<div class="form-group">
<button type="submit">
<img src="save-icon.png" alt="保存" width="16" height="16">
保存
</button>
<button type="reset" style="background-color: #f44336;">
重置表单
</button>
<button type="button" onclick="previewForm()">
预览
</button>
<input type="image" src="submit-button.png" alt="提交"
width="100" height="40"
formaction="/preview"
formmethod="GET"
formtarget="_blank">
</div>
七、响应式表单布局
1. CSS Grid 布局
css.form-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
}
@media (max-width: 600px) {
.form-grid {
grid-template-columns: 1fr;
}
}
2. Flexbox 布局
css.form-row {
display: flex;
flex-wrap: wrap;
margin: 0 -10px;
}
.form-col {
flex: 1 0 200px;
margin: 0 10px;
}
@media (max-width: 480px) {
.form-col {
flex: 1 0 100%;
}
}
3. 完整响应式表单示例
html<form class="responsive-form">
<div class="form-row">
<div class="form-col">
<label for="first-name">名字</label>
<input type="text" id="first-name" name="first_name">
</div>
<div class="form-col">
<label for="last-name">姓氏</label>
<input type="text" id="last-name" name="last_name">
</div>
</div>
<div class="form-row">
<div class="form-col">
<label for="email">电子邮箱</label>
<input type="email" id="email" name="email">
</div>
</div>
<div class="form-row">
<div class="form-col">
<label for="address">地址</label>
<input type="text" id="address" name="address">
</div>
</div>
<div class="form-row">
<div class="form-col">
<label for="city">城市</label>
<input type="text" id="city" name="city">
</div>
<div class="form-col">
<label for="zip">邮政编码</label>
<input type="text" id="zip" name="zip">
</div>
</div>
<div class="form-row">
<div class="form-col">
<button type="submit">提交</button>
</div>
</div>
</form>
<style>
.responsive-form {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.form-row {
display: flex;
flex-wrap: wrap;
margin: 0 -10px 15px;
}
.form-col {
flex: 1 0 200px;
margin: 0 10px;
}
.form-col label {
display: block;
margin-bottom: 5px;
}
.form-col input {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
@media (max-width: 600px) {
.form-col {
flex: 1 0 100%;
}
}
</style>
八、表单无障碍访问
1. 最佳实践
html<!-- 1. 正确的标签关联 -->
<label for="user-id">用户ID:</label>
<input type="text" id="user-id" name="user_id"
aria-describedby="id-help">
<p id="id-help">用户ID将用于登录系统</p>
<!-- 2. 必填字段标识 -->
<label for="required-field">
<span class="required">*</span> 必填字段
</label>
<input type="text" id="required-field" required
aria-required="true">
<!-- 3. 错误提示 -->
<div class="form-group">
<label for="credit-card">信用卡号</label>
<input type="text" id="credit-card" name="credit_card"
aria-invalid="false"
aria-errormessage="card-error">
<div id="card-error" class="error-message" role="alert"></div>
</div>
<!-- 4. 隐藏内容但可供屏幕阅读器读取 -->
<label for="search">
搜索
<span class="sr-only">(输入关键词后按回车)</span>
</label>
<input type="search" id="search" name="search">
<style>
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.error-message {
color: #d32f2f;
font-size: 0.875rem;
margin-top: 4px;
display: none;
}
input[aria-invalid="true"] {
border-color: #d32f2f;
}
</style>
<script>
const creditCardInput = document.getElementById('credit-card');
const errorElement = document.getElementById('card-error');
creditCardInput.addEventListener('blur', function() {
const value = this.value.replace(/\s+/g, '');
if (!/^\d{13,19}$/.test(value)) {
this.setAttribute('aria-invalid', 'true');
errorElement.textContent = '请输入有效的信用卡号(13-19位数字)';
errorElement.style.display = 'block';
} else {
this.setAttribute('aria-invalid', 'false');
errorElement.style.display = 'none';
}
});
</script>
九、表单性能优化
1. 延迟加载非关键表单
html<div class="lazy-form-section" data-src="/partials/address-form.html">
<!-- 地址表单将通过AJAX加载 -->
<button type="button" onclick="loadFormSection(this.parentElement)">
添加地址信息(可选)
</button>
</div>
<script>
function loadFormSection(container) {
const xhr = new XMLHttpRequest();
xhr.onload = function() {
container.innerHTML = this.responseText;
};
xhr.open('GET', container.getAttribute('data-src'));
xhr.send();
}
</script>
2. 表单自动保存
html<script>
// 自动保存表单数据到localStorage
document.getElementById('registerForm').addEventListener('input', function(e) {
if (e.target.name) {
const formData = JSON.parse(localStorage.getItem('formDraft') || '{}');
formData[e.target.name] = e.target.value;
localStorage.setItem('formDraft', JSON.stringify(formData));
}
});
// 页面加载时恢复数据
window.addEventListener('DOMContentLoaded', function() {
const savedData = JSON.parse(localStorage.getItem('formDraft') || '{}');
for (const [name, value] of Object.entries(savedData)) {
const element = document.querySelector(`[name="${name}"]`);
if (element) element.value = value;
}
});
// 提交时清除草稿
document.getElementById('registerForm').addEventListener('submit', function() {
localStorage.removeItem('formDraft');
});
</script>
3. 虚拟键盘优化
html<!-- 为移动设备优化输入模式 -->
<input type="text" inputmode="numeric" pattern="[0-9]*"
placeholder="请输入数字验证码">
<input type="text" inputmode="email"
placeholder="电子邮箱">
<input type="text" inputmode="url"
placeholder="网址">
<input type="text" inputmode="search"
placeholder="搜索...">
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>
<style>
:root {
--primary-color: #4285f4;
--error-color: #ea4335;
--success-color: #34a853;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: #333;
background-color: #f5f5f5;
margin: 0;
padding: 20px;
}
.form-container {
max-width: 800px;
margin: 0 auto;
background: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.form-header {
background-color: var(--primary-color);
color: white;
padding: 20px;
text-align: center;
}
.form-steps {
display: flex;
justify-content: space-between;
position: relative;
margin-bottom: 30px;
padding: 0 20px;
}
.form-steps::before {
content: '';
position: absolute;
top: 20px;
left: 0;
right: 0;
height: 2px;
background-color: #ddd;
z-index: 1;
}
.step {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
z-index: 2;
}
.step-number {
width: 40px;
height: 40px;
border-radius: 50%;
background-color: #ddd;
color: #999;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 5px;
font-weight: bold;
}
.step.active .step-number {
background-color: var(--primary-color);
color: white;
}
.step.completed .step-number {
background-color: var(--success-color);
color: white;
}
.step-title {
font-size: 0.875rem;
color: #666;
}
.step.active .step-title {
color: var(--primary-color);
font-weight: bold;
}
.form-body {
padding: 0 40px 30px;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 600;
}
.form-control {
width: 100%;
padding: 12px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 1rem;
box-sizing: border-box;
}
.form-control:focus {
border-color: var(--primary-color);
outline: none;
box-shadow: 0 0 0 2px rgba(66, 133, 244, 0.2);
}
.form-control.error {
border-color: var(--error-color);
}
.error-message {
color: var(--error-color);
font-size: 0.875rem;
margin-top: 5px;
display: none;
}
.form-footer {
display: flex;
justify-content: space-between;
padding: 20px 40px;
background-color: #f9f9f9;
border-top: 1px solid #eee;
}
.btn {
padding: 12px 24px;
border: none;
border-radius: 4px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: background-color 0.2s;
}
.btn-primary {
background-color: var(--primary-color);
color: white;
}
.btn-primary:hover {
background-color: #3367d6;
}
.btn-secondary {
background-color: white;
color: var(--primary-color);
border: 1px solid var(--primary-color);
}
.btn-secondary:hover {
background-color: #f0f4ff;
}
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.hidden {
display: none;
}
@media (max-width: 768px) {
.form-body, .form-footer {
padding-left: 20px;
padding-right: 20px;
}
.form-steps {
padding: 0 10px;
}
.step-title {
font-size: 0.75rem;
}
}
</style>
</head>
<body>
<div class="form-container">
<div class="form-header">
<h1>用户注册</h1>
<p>创建您的账户以享受更多服务</p>
</div>
<div class="form-steps">
<div class="step active" data-step="1">
<div class="step-number">1</div>
<div class="step-title">基本信息</div>
</div>
<div class="step" data-step="2">
<div class="step-number">2</div>
<div class="step-title">联系方式</div>
</div>
<div class="step" data-step="3">
<div class="step-number">3</div>
<div class="step-title">账户安全</div>
</div>
</div>
<form id="multiStepForm" novalidate>
<!-- 第一步:基本信息 -->
<div class="form-body" id="step1">
<div class="form-group">
<label for="firstName">名字</label>
<input type="text" class="form-control" id="firstName" name="firstName" required>
<div class="error-message" id="firstNameError">请输入您的名字</div>
</div>
<div class="form-group">
<label for="lastName">姓氏</label>
<input type="text" class="form-control" id="lastName" name="lastName" required>
<div class="error-message" id="lastNameError">请输入您的姓氏</div>
</div>
<div class="form-group">
<label for="gender">性别</label>
<select class="form-control" id="gender" name="gender" required>
<option value="">请选择</option>
<option value="male">男</option>
<option value="female">女</option>
<option value="other">其他</option>
</select>
<div class="error-message" id="genderError">请选择性别</div>
</div>
<div class="form-group">
<label>出生日期</label>
<div class="form-row">
<div class="form-col">
<select class="form-control" id="birthYear" name="birthYear" required>
<option value="">年份</option>
<!-- 动态生成年份选项 -->
</select>
</div>
<div class="form-col">
<select class="form-control" id="birthMonth" name="birthMonth" required>
<option value="">月份</option>
<!-- 动态生成月份选项 -->
</select>
</div>
<div class="form-col">
<select class="form-control" id="birthDay" name="birthDay" required>
<option value="">日期</option>
<!-- 动态生成日期选项 -->
</select>
</div>
</div>
<div class="error-message" id="birthDateError">请选择完整的出生日期</div>
</div>
</div>
<!-- 第二步:联系方式 -->
<div class="form-body hidden" id="step2">
<div class="form-group">
<label for="email">电子邮箱</label>
<input type="email" class="form-control" id="email" name="email" required>
<div class="error-message" id="emailError">请输入有效的电子邮箱</div>
</div>
<div class="form-group">
<label for="phone">手机号码</label>
<input type="tel" class="form-control" id="phone" name="phone"
pattern="[0-9]{11}" required>
<div class="error-message" id="phoneError">请输入11位手机号码</div>
</div>
<div class="form-group">
<label for="country">国家/地区</label>
<select class="form-control" id="country" name="country" required>
<option value="">请选择</option>
<option value="CN">中国</option>
<option value="US">美国</option>
<option value="JP">日本</option>
<option value="UK">英国</option>
</select>
<div class="error-message" id="countryError">请选择国家/地区</div>
</div>
<div class="form-group">
<label for="address">详细地址</label>
<textarea class="form-control" id="address" name="address" rows="3" required></textarea>
<div class="error-message" id="addressError">请输入详细地址</div>
</div>
</div>
<!-- 第三步:账户安全 -->
<div class="form-body hidden" id="step3">
<div class="form-group">
<label for="username">用户名</label>
<input type="text" class="form-control" id="username" name="username"
minlength="4" maxlength="16" required>
<div class="error-message" id="usernameError">用户名需4-16个字符</div>
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="password" class="form-control" id="password" name="password"
minlength="8" required
pattern="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).+$">
<div class="error-message" id="passwordError">
密码需至少8个字符,包含大小写字母和数字
</div>
</div>
<div class="form-group">
<label for="confirmPassword">确认密码</label>
<input type="password" class="form-control" id="confirmPassword" name="confirmPassword" required>
<div class="error-message" id="confirmPasswordError">两次输入的密码不一致</div>
</div>
<div class="form-group">
<div style="display: flex; align-items: center;">
<input type="checkbox" id="agreeTerms" name="agreeTerms" required>
<label for="agreeTerms" style="display: inline; margin-left: 10px; margin-bottom: 0;">
我已阅读并同意<a href="#">用户协议</a>和<a href="#">隐私政策</a>
</label>
</div>
<div class="error-message" id="agreeTermsError">必须同意条款才能继续</div>
</div>
</div>
<div class="form-footer">
<button type="button" class="btn btn-secondary" id="prevBtn" disabled>上一步</button>
<button type="button" class="btn btn-primary" id="nextBtn">下一步</button>
<button type="submit" class="btn btn-primary hidden" id="submitBtn">提交注册</button>
</div>
</form>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// 初始化步骤
let currentStep = 1;
const totalSteps = 3;
// 生成日期选项
function populateDateSelects() {
// 年份(1900-当前年份)
const yearSelect = document.getElementById('birthYear');
const currentYear = new Date().getFullYear();
for (let year = currentYear; year >= 1900; year--) {
const option = document.createElement('option');
option.value = year;
option.textContent = year;
yearSelect.appendChild(option);
}
// 月份
const monthSelect = document.getElementById('birthMonth');
for (let month = 1; month <= 12; month++) {
const option = document.createElement('option');
option.value = month;
option.textContent = month;
monthSelect.appendChild(option);
}
// 日期(根据选择的年月动态变化)
const daySelect = document.getElementById('birthDay');
document.getElementById('birthMonth').addEventListener('change', updateDays);
document.getElementById('birthYear').addEventListener('change', updateDays);
function updateDays() {
const selectedYear = parseInt(yearSelect.value);
const selectedMonth = parseInt(monthSelect.value);
// 清空现有选项
daySelect.innerHTML = '<option value="">日期</option>';
if (selectedYear && selectedMonth) {
const daysInMonth = new Date(selectedYear, selectedMonth, 0).getDate();
for (let day = 1; day <= daysInMonth; day++) {
const option = document.createElement('option');
option.value = day;
option.textContent = day;
daySelect.appendChild(option);
}
}
}
}
populateDateSelects();
// 步骤导航
const prevBtn = document.getElementById('prevBtn');
const nextBtn = document.getElementById('nextBtn');
const submitBtn = document.getElementById('submitBtn');
prevBtn.addEventListener('click', function() {
if (currentStep > 1) {
currentStep--;
updateStepView();
}
});
nextBtn.addEventListener('click', function() {
if (validateStep(currentStep)) {
if (currentStep < totalSteps) {
currentStep++;
updateStepView();
}
}
});
// 更新步骤显示
function updateStepView() {
// 隐藏所有步骤内容
for (let i = 1; i <= totalSteps; i++) {
document.getElementById(`step${i}`).classList.add('hidden');
}
// 显示当前步骤内容
document.getElementById(`step${currentStep}`).classList.remove('hidden');
// 更新步骤指示器
document.querySelectorAll('.step').forEach((step, index) => {
step.classList.toggle('active', index + 1 === currentStep);
step.classList.toggle('completed', index + 1 < currentStep);
});
// 更新按钮状态
prevBtn.disabled = currentStep === 1;
if (currentStep === totalSteps) {
nextBtn.classList.add('hidden');
submitBtn.classList.remove('hidden');
} else {
nextBtn.classList.remove('hidden');
submitBtn.classList.add('hidden');
}
}
// 表单验证
function validateStep(step) {
let isValid = true;
// 根据步骤验证不同字段
switch (step) {
case 1:
isValid = validateField('firstName') &&
validateField('lastName') &&
validateField('gender') &&
validateBirthDate();
break;
case 2:
isValid = validateField('email') &&
validateField('phone') &&
validateField('country') &&
validateField('address');
break;
case 3:
isValid =