HTML 表单(<form>)是网页与用户交互的核心组件,用于收集用户输入并提交到服务器。本教程从基础语法到高级应用,全面讲解表单的使用、属性、控件、验证、提交处理等内容。
一、表单核心概念
表单的本质是收集用户数据并通过 HTTP 协议提交到后端服务器(如 PHP/Node.js/Python 后端),核心流程:
- 用户在表单控件中输入/选择数据;
- 触发提交(点击按钮/回车);
- 浏览器将数据封装为请求(GET/POST)发送到指定后端地址;
- 后端接收并处理数据,返回响应(如跳转页面/提示信息)。
二、基础结构
一个完整的表单由 <form> 容器 + 表单控件(输入框、按钮等) + 标签(<label>)组成,基础语法:
html
<!-- 核心表单容器 -->
<form action="https://api.example.com/submit" method="POST">
<!-- 标签与输入框关联 -->
<label for="username">用户名:</label>
<input type="text" id="username" name="username">
<!-- 提交按钮 -->
<button type="submit">提交</button>
</form>
核心标签说明
| 标签 | 作用 | 必选属性 |
|---|---|---|
<form> |
表单容器,定义提交规则 | action、method |
<label> |
表单控件标签(提升可访问性) | for(与控件 id 关联) |
<input> |
通用输入控件(核心) | type、name(提交必备) |
<button> |
按钮(提交/重置/普通) | type(submit/reset/button) |
三、<form> 核心属性
<form> 标签的属性决定了表单的提交行为,是表单的"核心配置":
| 属性 | 取值/说明 |
|---|---|
action |
提交目标 URL(后端接口地址),默认提交到当前页面 |
method |
提交方式: - GET:数据拼接到 URL 后(?key=value),适合少量/非敏感数据 - POST:数据在请求体中,适合大量/敏感数据(如密码) |
enctype |
数据编码方式(仅 POST 有效): - application/x-www-form-urlencoded(默认):普通键值对 - multipart/form-data:文件上传专用 - text/plain:纯文本(极少用) |
target |
提交后响应的打开方式: - _self(默认):当前窗口 - _blank:新窗口 - _parent/_top:父/顶级框架 |
autocomplete |
是否开启自动补全:on(默认)/off |
novalidate |
禁用浏览器默认表单验证(布尔属性,写属性名即生效) |
示例:带文件上传的表单
html
<form
action="/upload"
method="POST"
enctype="multipart/form-data"
>
<label for="avatar">上传头像:</label>
<input type="file" id="avatar" name="avatar">
<button type="submit">上传</button>
</form>
四、表单控件全解析
表单控件是用户输入的载体,<input> 是最常用的控件,通过 type 属性实现不同功能,此外还有 <select>、<textarea> 等专用控件。
1. <input> 控件(按 type 分类)
| type 值 | 功能描述 | 关键属性 | 示例 |
|---|---|---|---|
text |
单行文本输入 | maxlength(最大长度)、placeholder(提示) |
<input type="text" name="username" placeholder="请输入用户名"> |
password |
密码输入(隐藏内容) | autocomplete="new-password"(禁用自动补全) |
<input type="password" name="pwd"> |
email |
邮箱输入(浏览器验证格式) | required(必填) |
<input type="email" name="email" required> |
tel |
电话号码输入 | - | <input type="tel" name="phone"> |
number |
数字输入(仅允许数字) | min/max(最小/大值)、step(步长) |
<input type="number" name="age" min="1" max="120"> |
range |
滑块选择数字 | min/max/step |
<input type="range" name="score" min="0" max="100" step="1"> |
date/time/datetime-local |
日期/时间/本地日期时间选择 | min/max |
<input type="datetime-local" name="birthday"> |
checkbox |
复选框(多选) | value(选中值)、checked(默认选中) |
<input type="checkbox" name="hobby" value="reading" checked> 阅读 |
radio |
单选框(互斥多选) | name 相同则互斥、checked |
<input type="radio" name="gender" value="male" checked> 男<br><input type="radio" name="gender" value="female"> 女 |
file |
文件选择 | multiple(多选文件)、accept(文件类型) |
<input type="file" name="file" multiple accept=".jpg,.png"> |
hidden |
隐藏输入(用户不可见,传递固定值) | value |
<input type="hidden" name="token" value="123456"> |
submit |
提交按钮(触发表单提交) | value(按钮文字) |
<input type="submit" value="提交表单"> |
reset |
重置按钮(清空表单数据) | - | <input type="reset" value="重置"> |
button |
普通按钮(无默认行为) | - | <input type="button" value="点击触发JS" onclick="alert('Hello')"> |
2. 专用控件
(1)<textarea>:多行文本输入
适合大段文字(如留言、备注),核心属性:
rows:默认显示行数;cols:默认列数;maxlength:最大字符数;placeholder:提示文本。
示例:
html
<label for="remark">备注:</label>
<textarea
id="remark"
name="remark"
rows="5"
cols="30"
placeholder="请输入备注(最多100字)"
maxlength="100"
></textarea>
(2)<select> + <option>:下拉选择框
<select>:下拉容器,name为提交的键名;<option>:下拉选项,value为提交值,selected表示默认选中;multiple:开启多选(按住 Ctrl/Command 选择)。
示例:
html
<label for="city">选择城市:</label>
<select id="city" name="city">
<option value="">请选择</option>
<option value="beijing" selected>北京</option>
<option value="shanghai">上海</option>
<option value="guangzhou">广州</option>
</select>
<!-- 多选下拉 -->
<select name="cities" multiple>
<option value="beijing">北京</option>
<option value="shanghai">上海</option>
</select>
(3)<button>:按钮(更灵活)
<button> 比 <input type="button"> 更灵活,可包含HTML内容(如图标+文字),核心 type 属性:
submit:提交表单(默认);reset:重置表单;button:普通按钮(无默认行为)。
示例:
html
<!-- 带图标的提交按钮 -->
<button type="submit">
<svg width="16" height="16">...</svg>
提交表单
</button>
<!-- 普通按钮(触发JS) -->
<button type="button" onclick="resetForm()">重置</button>
五、表单验证
表单验证用于确保用户输入符合规则,分为浏览器原生验证 和自定义JS验证,优先使用原生验证(简洁、性能好),复杂场景补充JS验证。
1. 原生验证(HTML5 内置)
通过控件属性实现,无需JS,浏览器自动验证,验证失败则阻止提交。
| 验证属性 | 作用 | 适用控件 |
|---|---|---|
required |
必填项(不能为空) | 所有输入控件 |
min/max |
数字/日期的最小/大值 | number、range、date等 |
minlength/maxlength |
文本最小/大长度 | text、textarea、email等 |
pattern |
正则表达式验证 | 文本类控件 |
type="email"/"url" |
内置格式验证 | email、url |
示例:带原生验证的表单
html
<form action="/submit" method="POST">
<!-- 必填 + 最小长度 -->
<label for="username">用户名:</label>
<input
type="text"
id="username"
name="username"
required
minlength="3"
maxlength="10"
placeholder="3-10个字符"
>
<!-- 正则验证手机号(11位数字) -->
<label for="phone">手机号:</label>
<input
type="tel"
id="phone"
name="phone"
required
pattern="^1[3-9]\d{9}$"
title="请输入正确的11位手机号"
>
<!-- 数字范围验证 -->
<label for="age">年龄:</label>
<input
type="number"
id="age"
name="age"
min="18"
max="60"
required
>
<button type="submit">提交</button>
</form>
2. 自定义验证(JS)
原生验证无法满足复杂场景(如密码强度、两次密码一致)时,使用JS验证,核心步骤:
- 监听表单提交事件(
submit); - 阻止默认提交行为(
event.preventDefault()); - 获取控件值并验证;
- 验证通过则手动提交表单,否则提示错误。
示例:JS验证密码一致性
html
<form id="registerForm" action="/register" method="POST">
<label for="pwd">密码:</label>
<input type="password" id="pwd" name="pwd" required minlength="6">
<label for="pwdConfirm">确认密码:</label>
<input type="password" id="pwdConfirm" required>
<div id="errorTip" style="color: red; display: none;"></div>
<button type="submit">注册</button>
</form>
<script>
const form = document.getElementById('registerForm');
const pwd = document.getElementById('pwd');
const pwdConfirm = document.getElementById('pwdConfirm');
const errorTip = document.getElementById('errorTip');
form.addEventListener('submit', (e) => {
// 阻止默认提交
e.preventDefault();
// 验证密码一致性
if (pwd.value!== pwdConfirm.value) {
errorTip.textContent = '两次密码输入不一致';
errorTip.style.display = 'block';
return;
}
// 验证通过,手动提交表单
form.submit();
});
// 输入时清空错误提示
pwdConfirm.addEventListener('input', () => {
errorTip.style.display = 'none';
});
</script>
3. 自定义验证消息
通过 setCustomValidity() 自定义原生验证的提示语:
html
<input type="text" id="username" required oninput="checkUsername(this)">
<script>
function checkUsername(el) {
if (el.value.includes(' ')) {
el.setCustomValidity('用户名不能包含空格');
} else {
el.setCustomValidity(''); // 清空自定义提示
}
}
</script>
六、表单提交处理
1. 传统提交(页面跳转)
表单提交后,浏览器跳转到 action 指定的URL,后端处理后返回新页面(如成功提示页)。
- 优点:简单,无需JS;
- 缺点:用户体验差(页面刷新)。
2. AJAX 提交(无刷新)
通过JS的 XMLHttpRequest 或 fetch 提交表单数据,实现无刷新提交,是现代前端的主流方式。
示例:Fetch 提交表单数据
html
<form id="ajaxForm">
<label for="username">用户名:</label>
<input type="text" id="username" name="username" required>
<button type="submit">提交</button>
</form>
<div id="result"></div>
<script>
const form = document.getElementById('ajaxForm');
const result = document.getElementById('result');
form.addEventListener('submit', async (e) => {
e.preventDefault(); // 阻止传统提交
// 构建表单数据
const formData = new FormData(form);
try {
// 发送POST请求
const res = await fetch('/api/submit', {
method: 'POST',
body: formData // 自动处理enctype
});
const data = await res.json();
if (res.ok) {
result.textContent = '提交成功:' + data.msg;
result.style.color = 'green';
form.reset(); // 重置表单
} else {
result.textContent = '提交失败:' + data.msg;
result.style.color = 'red';
}
} catch (err) {
result.textContent = '网络错误:' + err.message;
result.style.color = 'red';
}
});
</script>
3. 表单数据序列化
若需将表单数据转为JSON格式提交(而非FormData),可手动序列化:
javascript
// 序列化表单为JSON
function serializeForm(form) {
const obj = {};
const formData = new FormData(form);
for (const [key, value] of formData.entries()) {
obj[key] = value;
}
return obj;
}
// 提交JSON数据
fetch('/api/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(serializeForm(form))
});
七、表单可访问性(A11Y)
良好的表单可访问性能提升残障用户体验(如屏幕阅读器),核心规则:
-
<label>与控件关联 :通过for属性绑定控件id,避免仅用文字占位;html<!-- 正确 --> <label for="email">邮箱:</label> <input type="email" id="email" name="email"> <!-- 错误(无关联) --> <div>邮箱:<input type="email" name="email"></div> -
分组控件用
<fieldset>+<legend>:如单选框组、表单分区;html<fieldset> <legend>性别</legend> <input type="radio" id="male" name="gender" value="male"> <label for="male">男</label> <input type="radio" id="female" name="gender" value="female"> <label for="female">女</label> </fieldset> -
提供清晰的错误提示 :错误信息关联到对应控件(用
aria-describedby);html<input type="tel" id="phone" name="phone" aria-describedby="phoneError"> <div id="phoneError" style="color: red;">请输入正确的手机号</div> -
禁用控件添加说明 :用
aria-disabled或title说明禁用原因; -
支持键盘操作:确保Tab键可遍历所有控件,Enter键可提交。
八、常见问题与解决方案
1. 表单提交后页面刷新
- 解决方案:使用AJAX提交(阻止默认行为);
- 备选:
action指向当前页面,后端处理后返回原页面(体验差)。
2. 文件上传失败
- 检查
enctype是否为multipart/form-data; - 检查后端是否支持文件接收;
- 检查
accept属性是否限制了正确的文件类型。
3. 原生验证提示语不友好
- 解决方案:用
setCustomValidity()自定义提示; - 备选:禁用原生验证(
novalidate),完全用JS验证。
4. 单选框/复选框默认选中不生效
- 确保
checked属性正确添加(无需赋值,写checked即可); - 单选框确保
name属性相同(互斥)。
九、总结
HTML 表单是前端交互的核心,掌握以下要点即可应对绝大多数场景:
- 理解
<form>的核心属性(action/method/enctype); - 熟练使用各类表单控件(
<input>/<select>/<textarea>); - 掌握原生验证 + JS 自定义验证;
- 学会AJAX无刷新提交(现代前端主流);
- 重视可访问性,提升全用户体验。