跳转至

表单

表单就像一张纸质问卷——有填空题(input)、选择题(radio/checkbox)、下拉题(select)和问答题(textarea)。浏览器把用户填写的数据打包发送给服务器,整个 Web 交互的核心就在于此。

本文你会学到:

  • 🎯 form 容器的作用与核心属性
  • 🔧 各类输入控件(文本、选择、多行文本)的用法
  • 📦 用 fieldset 对表单进行分组
  • 🆕 HTML5 新增控件(datalistoutputprogressmeter
  • ✅ 浏览器内置表单验证的使用方式

📋 form 标签做了什么?

form 容器

form 是所有表单控件的容器,它本身不显示任何内容,但定义了数据往哪发、怎么发

form 基本结构
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>form 基本结构</title>
</head>
<body>
<!-- form 基本结构:form 容器的三个核心属性 action、method、enctype -->
<form action="/submit" method="POST" enctype="multipart/form-data">
  <!-- 表单控件放在这里 -->
</form>
</body>
</html>

MDN

form 的三个核心属性:

属性 说明 示例
action 提交目标 URL action="/api/login"
method 请求方式:GET(查询)或 POST(提交) method="POST"
enctype 编码类型,上传文件时需设为 multipart/form-data enctype="multipart/form-data"

method 怎么选?

  • GET:数据附在 URL 上(?name=value),适合搜索、筛选等无副作用的操作
  • POST:数据放在请求体中,适合登录、注册等有数据修改的操作
搜索用 GET
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>搜索用 GET</title>
</head>
<body>
<!-- 搜索用 GET:数据附在 URL 上,适合无副作用操作 -->
<form action="/search" method="GET">
  <input type="text" name="q" placeholder="搜索...">
  <button type="submit">搜索</button>
</form>
<!-- 提交后 URL 变为 /search?q=用户输入的内容 -->
</body>
</html>
登录用 POST
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>登录用 POST</title>
</head>
<body>
<!-- 登录用 POST:数据放在请求体中,适合有数据修改的操作 -->
<form action="/login" method="POST">
  <input type="text" name="username" placeholder="用户名">
  <input type="password" name="password" placeholder="密码">
  <button type="submit">登录</button>
</form>
</body>
</html>

📝 用户怎么填数据?——基础输入控件

input 标签与 type 属性

input 是最常用的表单控件——一个自闭合标签,通过 type 属性决定它的外观和行为。

最常用的几种 input
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>最常用的几种 input</title>
</head>
<body>
<!-- 最常用的几种 input:text、password、email、number、date、file -->
<input type="text" name="username" placeholder="请输入用户名"><br><br>
<input type="password" name="pwd" placeholder="请输入密码"><br><br>
<input type="email" name="mail" placeholder="请输入邮箱"><br><br>
<input type="number" name="age" placeholder="年龄" min="0" max="150"><br><br>
<input type="date" name="birthday"><br><br>
<input type="file" name="avatar" accept="image/*">
</body>
</html>

💡 type="text" 是默认值,写不写效果一样,但显式写出更清晰

type="file" 有两个特有属性:

file 输入的特有属性
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>file 输入的特有属性</title>
</head>
<body>
<!-- file 输入的特有属性:multiple 和 accept -->
<!-- multiple:允许选择多个文件 -->
<input type="file" name="files" multiple><br><br>

<!-- accept:限制文件类型 -->
<input type="file" name="photo" accept="image/png,image/jpeg"><br><br>

<!-- 常见 accept 写法 -->
<input type="file" accept=".pdf"><br> <!-- 只允许 PDF -->
<br>
<input type="file" accept="image/*"><br> <!-- 所有图片 -->
<br>
<input type="file" accept="video/*"> <!-- 所有视频 -->
</body>
</html>

MDN

除上述类型外,input 还支持以下 type 值:searchtelurlhiddensubmitresetbuttonimagerangecolormonthtimeweekdatetime-local。完整列表见文末速查表。

input 通用属性

不管 type 是什么,以下属性对所有 input 都适用:

input 通用属性演示
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>input 通用属性演示</title>
</head>
<body>
<!-- input 通用属性演示:name、placeholder、maxlength、autocomplete -->
<input
  type="text"
  name="username"
  placeholder="请输入用户名"
  maxlength="20"
  autocomplete="on"
>
</body>
</html>
属性 说明 示例
name 字段名,提交时作为 key name="email"email=xxx@xx.com
placeholder 输入框为空时显示的提示文字 placeholder="请输入"
maxlength 限制最大输入字符数 maxlength="50"
autocomplete 是否允许浏览器自动填充 autocomplete="off"
accesskey 快捷键(按 Alt+字母 聚焦) accesskey="u"Alt+U

⚠️ 没有 name 属性的输入框,数据不会随表单提交。

textarea 多行文本

input 只能输入单行文本。需要输入多行内容(如评论、备注)时,使用 textarea

textarea 多行文本框
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>textarea 多行文本框</title>
</head>
<body>
<!-- textarea 多行文本框:rows、cols、placeholder、maxlength -->
<textarea
  name="comment"
  rows="5"
  cols="40"
  placeholder="请输入评论..."
  maxlength="500"
></textarea>
</body>
</html>

💡 textarea 的内容写在开始标签和结束标签之间,而不是 value 属性里:

textarea 的默认值
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>textarea 的默认值</title>
</head>
<body>
<!-- textarea 的默认值:内容写在标签之间,而非 value 属性 -->
<textarea name="bio">这里写默认内容</textarea>
<br><br>
<!-- 错误:textarea 没有 value 属性 -->
<textarea name="bio" value="默认值"></textarea>
</body>
</html>

🔘 怎么让用户做选择?

select 下拉选择

select + option 组合出一个下拉选择框——适合选项较多、占用空间又不能太大的场景。

select 下拉选择框
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>select 下拉选择框</title>
</head>
<body>
<!-- select 下拉选择框:option 定义选项 -->
<select name="city">
  <option value="">请选择城市</option>
  <option value="beijing">北京</option>
  <option value="shanghai">上海</option>
  <option value="guangzhou">广州</option>
  <option value="shenzhen">深圳</option>
</select>
</body>
</html>

常用增强属性:

select 的实用技巧
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>select 的实用技巧</title>
</head>
<body>
<!-- select 的实用技巧:默认选中、禁用、分组、多选 -->
<p>默认选中某一项:</p>
<select>
  <option value="beijing">北京</option>
  <option value="shanghai" selected>上海</option>
  <option value="guangzhou">广州</option>
</select>

<br><br>
<p>禁用某一项(置灰不可选):</p>
<select>
  <option value="" disabled>请选择城市</option>
  <option value="beijing">北京</option>
  <option value="shanghai">上海</option>
</select>

<br><br>
<p>分组显示(optgroup):</p>
<select name="province">
  <optgroup label="华东地区">
    <option value="shanghai">上海</option>
    <option value="jiangsu">江苏</option>
  </optgroup>
  <optgroup label="华南地区">
    <option value="guangdong">广东</option>
    <option value="fujian">福建</option>
  </optgroup>
</select>

<br><br>
<p>多选(按住 Ctrl 点击):</p>
<select name="hobbies" multiple size="4">
  <option value="reading">阅读</option>
  <option value="coding">编程</option>
  <option value="gaming">游戏</option>
  <option value="music">音乐</option>
</select>
</body>
</html>

radio 单选与 checkbox 多选

radio 是单选按钮——同组(相同 name)中只能选一个。checkbox 是复选框——可以同时选多个。

radio 单选
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>radio 单选</title>
</head>
<body>
<!-- radio 单选:同 name 互斥,只能选一个 -->
<p>性别:</p>
<label><input type="radio" name="gender" value="male"></label>
<label><input type="radio" name="gender" value="female"></label>
<label><input type="radio" name="gender" value="other"> 其他</label>
</body>
</html>
checkbox 多选
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>checkbox 多选</title>
</head>
<body>
<!-- checkbox 多选:可以同时选多个 -->
<p>爱好:</p>
<label><input type="checkbox" name="hobby" value="reading"> 阅读</label>
<label><input type="checkbox" name="hobby" value="coding"> 编程</label>
<label><input type="checkbox" name="hobby" value="gaming"> 游戏</label>
</body>
</html>

⚠️ radio 的分组靠的是 name 属性——name 相同的 radio 互斥:

两组独立的 radio
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>两组独立的 radio</title>
</head>
<body>
<!-- 两组独立的 radio:name 不同则互不影响 -->
<p>配送方式:</p>
<label><input type="radio" name="shipping" value="express"> 快递</label>
<label><input type="radio" name="shipping" value="pickup"> 自提</label>

<p>支付方式:</p>
<label><input type="radio" name="payment" value="alipay"> 支付宝</label>
<label><input type="radio" name="payment" value="wechat"> 微信</label>
<!-- shipping 和 payment 是两个独立的组,互不影响 -->
</body>
</html>

设置默认选中用 checked 属性:

默认选中
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>默认选中</title>
</head>
<body>
<!-- 默认选中:radio 和 checkbox 用 checked 属性 -->
<p>性别:</p>
<label><input type="radio" name="gender" value="male" checked></label>
<label><input type="radio" name="gender" value="female"></label>

<br>
<p>同意条款:</p>
<label><input type="checkbox" name="agree" value="yes" checked> 我已阅读并同意</label>
</body>
</html>

🖱️ 点击按钮时发生了什么?

button 三种类型

button 元素有三种 type,行为完全不同:

button 的三种类型
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>button 的三种类型</title>
</head>
<body>
<!-- button 的三种类型:submit、reset、button -->
<!-- type="submit":提交表单(默认值) -->
<button type="submit">提交</button>

<!-- type="reset":重置表单为初始值 -->
<button type="reset">重置</button>

<!-- type="button":普通按钮,不做任何事,需 JS 绑定逻辑 -->
<button type="button" onclick="handleClick()">点击我</button>
</body>
</html>

MDN

<button><form> 内部时,type 默认为 submit。这意味着如果忘了写 type,点击按钮会意外提交表单。最佳实践:始终显式声明 type

✅ 始终声明 type

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>始终声明 type</title>
</head>
<body>
<!-- 始终声明 type:button 在 form 中默认 type 为 submit -->
<button type="button">取消</button>
</body>
</html>

❌ 忘了 type 在 form 中会导致意外提交:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>忘了 type 导致意外提交</title>
</head>
<body>
<!-- 忘了 type 导致意外提交:button 在 form 中默认 type 为 submit -->
<!-- 用户点"取消"却触发了表单提交 -->
<form action="/api/order">
  <button>取消</button>
  <button type="submit">确认下单</button>
</form>
</body>
</html>

你也可以用 input 来创建按钮,但 button 更灵活——它支持图标、富文本等内部内容:

button vs input 按钮
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>button vs input 按钮</title>
</head>
<body>
<!-- button vs input 按钮:button 支持图标和富文本,input 只能纯文本 -->
<!-- button 可以放图标、文字 -->
<button type="submit">
  发送
</button>

<!-- input 按钮只能放纯文本 -->
<input type="submit" value="发送">
</body>
</html>

label 关联文本

label 为表单控件提供可点击的文本标签。它的两个好处:

  1. 可访问性:屏幕阅读器能读出标签,帮助视障用户理解输入框用途
  2. 点击体验:点击标签文本就能聚焦对应的输入框,点选范围更大

方式一:for + id 关联

label 方式一:for + id
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>label 方式一:for + id</title>
</head>
<body>
<!-- label 方式一:for + id 关联,点击文本聚焦输入框 -->
<label for="username">用户名</label>
<input type="text" id="username" name="username">
<!-- 点击"用户名"三个字,输入框会获得焦点 -->
</body>
</html>

方式二:直接把 input 放在 label 里面

label 方式二:包含 input
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>label 方式二:包含 input</title>
</head>
<body>
<!-- label 方式二:直接把 input 放在 label 里面 -->
<label>
  用户名
  <input type="text" name="username">
</label>
<!-- 点击"用户名"文字或输入框本身都能聚焦 -->
</body>
</html>

💡 方式二代码更简洁,但方式一更灵活——labelinput 不需要在 DOM 结构上相邻。实际项目中两种都很常见,选择适合当前场景的即可。

📦 表单太长怎么分组?

fieldset 与 legend

当表单字段很多时,用 fieldset 把相关的控件分组,再用 legend 给每组起个标题。浏览器会自动给 fieldset 加上边框。

fieldset 分组表单
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>fieldset 分组表单</title>
</head>
<body>
<!-- fieldset 分组表单:legend 标题,浏览器自动加边框 -->
<form action="/register" method="POST">
  <!-- 第一组:个人信息 -->
  <fieldset>
    <legend>个人信息</legend>
    <label for="name">姓名</label>
    <input type="text" id="name" name="name" placeholder="请输入姓名">

    <label for="email">邮箱</label>
    <input type="email" id="email" name="email" placeholder="请输入邮箱">
  </fieldset>

  <!-- 第二组:账户设置 -->
  <fieldset>
    <legend>账户设置</legend>
    <label for="password">密码</label>
    <input type="password" id="password" name="password" placeholder="请输入密码">

    <label for="role">角色</label>
    <select id="role" name="role">
      <option value="user">普通用户</option>
      <option value="admin">管理员</option>
    </select>
  </fieldset>

  <button type="submit">注册</button>
</form>
</body>
</html>

MDN

fieldset 还有一个 disabled 属性——设置后,组内**所有**表单控件都会被禁用,无需逐个设置。这在表单分步填写、条件显示等场景下很实用。

fieldset 禁用整组控件
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>fieldset 禁用整组控件</title>
</head>
<body>
<!-- fieldset 禁用整组控件:disabled 属性让组内所有控件不可编辑 -->
<fieldset disabled>
  <legend>高级设置(当前不可编辑)</legend>
  <input type="text" name="api_key" placeholder="API Key">
  <input type="text" name="webhook" placeholder="Webhook URL">
</fieldset>
</body>
</html>

🆕 HTML5 带来了哪些新控件?

datalist 输入建议

datalist 配合 input 提供输入建议列表——用户既可以从列表中选择,也可以自己输入,灵活性比 select 更高

datalist 输入建议
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>datalist 输入建议</title>
</head>
<body>
<!-- datalist 输入建议:配合 input 提供可选建议列表 -->
<input type="text" name="browser" list="browser-list" placeholder="输入或选择浏览器">
<datalist id="browser-list">
  <option value="Chrome">
  <option value="Firefox">
  <option value="Safari">
  <option value="Edge">
</datalist>
</body>
</html>

💡 用户输入时,浏览器会自动显示匹配的建议。但用户完全可以输入一个不在列表中的值——这就是它和 select 的本质区别。

output / progress / meter

MDN

HTML5 引入了几个用于展示数据的语义化标签,虽然不属于传统「表单控件」,但常配合表单使用。

output——计算结果输出

output 显示计算结果
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>output 显示计算结果</title>
</head>
<body>
<!-- output 显示计算结果:配合 oninput 事件实时计算 -->
<form oninput="result.value = Number(a.value) + Number(b.value)">
  <input type="number" name="a" value="10"> +
  <input type="number" name="b" value="20"> =
  <output name="result" for="a b">30</output>
</form>
</body>
</html>

progress——进度条

progress 进度条
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>progress 进度条</title>
</head>
<body>
<!-- progress 进度条:确定性(已知百分比)和不确定性(加载中) -->
<!-- 确定性进度(已知百分比) -->
<label>上传进度:<progress value="70" max="100">70%</progress></label>

<br><br>
<!-- 不确定性进度(加载中,不知道多久) -->
<label>加载中:<progress>请稍候...</progress></label>
</body>
</html>

meter——度量/标量值

meter 度量标签
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>meter 度量标签</title>
</head>
<body>
<!-- meter 度量标签:low/high/optimum 定义颜色区间 -->
<meter value="0.7" min="0" max="1" low="0.3" high="0.8" optimum="0.5">
  70%
</meter>
</body>
</html>

progressmeter 的区别:

progress meter
语义 任务进度(从 0 到完成) 某个度量值(如磁盘用量、考试成绩)
方向 只会从低到高 可以高也可以低
颜色变化 统一样式 根据 low/high/optimum 自动变色

✅ 不写 JavaScript 也能验证?——内置表单验证

内置验证属性

HTML5 提供了浏览器原生验证——不需要写 JavaScript,直接在标签上加属性就能实现基本的表单校验。

常用验证属性
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>常用验证属性</title>
</head>
<body>
<!-- 常用验证属性:required、minlength、maxlength、min、max、pattern、type -->
<form>
  <!-- required:必填 -->
  <input type="text" name="username" required placeholder="用户名(必填)"><br><br>

  <!-- minlength / maxlength:长度限制 -->
  <input type="text" name="code" minlength="6" maxlength="6" placeholder="6位验证码"><br><br>

  <!-- min / max:数值范围 -->
  <input type="number" name="age" min="0" max="150" placeholder="年龄(0-150)"><br><br>

  <!-- pattern:正则表达式校验 -->
  <input type="text" name="phone" pattern="[0-9]{11}" placeholder="11位手机号"><br><br>

  <!-- type 本身就是验证:email 类型会检查邮箱格式 -->
  <input type="email" name="email" placeholder="邮箱(自动校验格式)"><br><br>

  <button type="submit">提交</button>
</form>
</body>
</html>

验证属性速查:

属性 说明 示例
required 必填字段 required
pattern 正则校验 pattern="[A-Za-z]{3,}"
min / max 数值最小/最大值 min="0" max="100"
minlength / maxlength 字符串最小/最大长度 minlength="6"
type 类型自带验证 type="email"type="url"type="number"

⚠️ 浏览器原生验证的提示文案是英文的,且样式无法完全自定义。正式项目中通常用 JavaScript 库(如 vee-validatezod)实现更灵活的验证。

💡 novalidate 属性可以关闭表单的浏览器验证——当你想完全用 JS 控制验证逻辑时使用:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>novalidate 关闭浏览器验证</title>
</head>
<body>
<!-- novalidate 关闭浏览器验证:完全由 JS 控制验证逻辑 -->
<form novalidate>
  <!-- 浏览器不会进行任何验证,全部由 JS 处理 -->
  <input type="text" name="username" placeholder="用户名"><br><br>
  <input type="email" name="email" placeholder="邮箱(不会校验格式)"><br><br>
  <button type="submit">提交</button>
</form>
</body>
</html>

📊 input 类型速查表

type 用途 渲染效果
text 单行文本(默认) 普通输入框
password 密码输入(内容遮蔽) ••••••
email 邮箱(自动校验格式) 普通输入框 + 验证
number 数字(含增减按钮) 数字输入框
tel 电话号码 普通输入框
url 网址(自动校验格式) 普通输入框 + 验证
search 搜索框(部分浏览器带清除按钮) 带圆角的输入框
date 日期选择(年-月-日) 日期选择器
time 时间选择(时:分) 时间选择器
month 月份选择(年-月) 月份选择器
week 周数选择(年-第W周) 周选择器
datetime-local 日期+时间(无时区) 日期时间选择器
color 颜色选择器 色板弹窗
range 滑块 可拖动的滑块条
file 文件选择 "选择文件"按钮
hidden 隐藏字段(用户不可见,数据会提交) 不渲染
submit 提交按钮 普通按钮
reset 重置按钮 普通按钮
button 普通按钮(需 JS 绑定事件) 普通按钮
image 图像提交按钮(以图片形式) 可点击的图片
checkbox 复选框 ☑ 方框
radio 单选按钮 ◉ 圆点