12表单高级话题
05、09 两课把表单的「骨头」搭好了:控件家族、HTML5 新属性、浏览器原生校验。 这一课往血肉方向走——fieldset / legend 把表单拆成「分组卡片」、 :user-invalid / :user-valid 让浏览器智能识别「用户修改过」的状态、 文件上传的 accept / multiple / 拖拽预览, 还有可访问性、复合按钮这一类「正经项目才用得到」的话题。
一、fieldset 与 legend:表单的「分组卡片」
09 课简单提过 ;本课把它玩出花来——它嵌套、
disabled 一键禁用组内全部控件,再配合 做出「卡顶标签」效果。
这些是设计稿里分组注册卡、信息面板的常见写法。
| 标签 / 属性 | 作用 | 备注 |
|---|---|---|
| 把一组相关控件圈成一个分组框 | 语义化、读屏软件会念「分组」 | |
| 分组框的标题,必须是 fieldset 的第一个孩子 | 默认渲染在边框左上角 | |
| fieldset[disabled] | 一键禁用组内所有表单控件 | 含嵌套 fieldset 也会一起禁用 |
| fieldset > legend { float: left } | 让 legend「浮」到边框上做成卡顶标签 | 经典 UI 小技巧 |
| fieldset 嵌套 | 大分组里再分小组(两层标签) | 读屏会按层级逐层念 |
disabled 写上,看「联系偏好」整组置灰——表单里有需要「按条件启用」的场景,fieldset 一次搞定border / border-radius / padding
的重置样式,保留它的语义(读屏能识别分组),视觉外观交给设计稿。
legend { float: left } 这个小技巧让 legend 浮到边框上,
配合背景色直接变成「卡顶标签」,比纯文字标题精致得多。二、用户态伪类 :user-invalid 与 :user-valid
09 课学过 :invalid / :valid。但它有个反人类的设计:
用户还没碰过表单,一加载就红(因为空 required 字段就是 invalid)。
HTML 给出的解药是 :user-invalid / :user-valid——
只有用户真正修改过字段、且当前值不合法时才上色。
| 伪类 | 触发时机 | 用户体验 | 推荐场景 |
|---|---|---|---|
| :invalid | 只要字段当前不合法(含刚加载的空 required) | 页面一打开就红一片,「还没填你就说我错?」 | 提交后整体校验、错误汇总 |
| :user-invalid | 用户真正改过且当前值不合法 | 改过之后才红,安静友好 | 现代表单首选:实时字段级提示 |
| :valid / :user-valid | 字段当前合法 | 绿色对勾 / 高亮,常见于填对之后 | 辅助性视觉反馈 |
:invalid 一打开就红、右 :user-invalid 必须用户改过才红——点邮箱框、随便输入再删空,看右侧「安静」、左侧「立刻发火」:user-invalid(改过再提示,温柔不打扰);
提交按钮的可用性、错误汇总用 :invalid(只要表单里有错就拦截提交)。
这样既安静又安全,是现代表单的事实标准。:user-invalid 在 2023 年后才被主流浏览器全面支持,
Safari / Chrome / Firefox / Edge 都已落地,但极老版本(IE / 旧 Edge)不支持。
关键场景建议 :invalid 和 :user-invalid 同时定义,
不支持前者兜底。三、placeholder 与输入提示的取舍
08 课无障碍里讲过:placeholder 永远不能当 label 用——
用户开始输入文字后它就消失,:placeholder-shown 还可针对「空字段」做样式。
placeholder 真正该做的,是给一个示例值。
| 角色 | 用法 | 对不对 | 原因 |
|---|---|---|---|
| 示例值 | placeholder="如 zhang@example.com" | ✓ 推荐 | 有 label 在,placeholder 只补个例子 |
| 格式提示 | placeholder="11 位手机号,1 开头" | ✓ 可用 | 补充说明字段格式 |
| 当 label | placeholder="请输入邮箱"(无 label) | ✗ 反模式 | 对比度差、信息会消失、无障碍灾难 |
:placeholder-shown 用来「当字段为空时」的样式钩子——
比如「输入框为空时显示一个图标、开始输入就隐藏」。
它判断的是 placeholder 文本是否还在展示,不是字段是否有值。四、文件上传:accept / multiple / 拖拽
是表单里唯一一类必须靠 JS 才能玩出花的控件——
浏览器负责选文件,JS 负责「预览 / 上传 / 拖拽 / 进度」。本节只看 HTML 部分能做的事。
| 属性 | 作用 | 示例 |
|---|---|---|
| accept | 限定文件类型,文件选择器自动过滤 | accept="image/*" 只显示图片 |
| accept 细分 | 用 MIME 或扩展名精确到具体格式 | accept=".jpg,.png,image/jpeg" |
| multiple | 允许多选(按住 Ctrl / Cmd) | |
| capture | 移动端直接调起摄像头 / 麦克风 | capture="environment" 后置摄像头 |
| required | 必填,提交时校验至少有一个文件 | 配合 multiple 时校验总有效文件 ≥1 |
accept 改成 "video/*",看文件选择器从「图」切到「视频」过滤;加 multiple 看选择器支持多选onclick、不嵌 JS。
拖拽预览、即时缩略图、上传进度这类功能必须 JS 介入——
它们属于 HTML 之后的「JavaScript 篇章」。但请记住:HTML 部分能做的
就已经做了(限定类型 / 多选 / 必填),JS 只是把这些体验串起来。五、inputmode 与 autocomplete 进阶
移动端表单体验好不好,键盘弹什么是关键。
inputmode 决定虚拟键盘样式,autocomplete 决定浏览器是否自动填、
填什么类型的内容,autocapitalize 决定是否首字母大写。
| inputmode | 键盘 | 适用字段 |
|---|---|---|
| text | 普通键盘(默认) | 用户名、地址、备注 |
| numeric | 纯数字键盘 | 验证码、数量 |
| decimal | 数字 + 小数点 | 价格、金额 |
| tel | 拨号键盘(带 * #) | 电话号码 |
| 英文键盘 + @ 键 | 邮箱地址 | |
| url | 英文键盘 + / .com 键 | 网址 |
| search | 键盘右下角是「搜索」按钮 | 搜索框 |
| autocomplete | 含义 | 典型场景 |
|---|---|---|
| on / off | 开 / 关浏览器记忆 | 通用开关 |
| name | 填全名 | 收货人、注册姓名 |
| 填邮箱 | 登录邮箱、联系邮箱 | |
| current-password | 填当前密码 | 登录、改密 |
| new-password | 填新密码(不记忆) | 注册、改密 |
| one-time-code | 填短信验证码 | 两步验证、找回密码 |
| tel / tel-national | 填电话 | 收货电话 |
| off | 明确关闭记忆 | 一次性邀请码、测试答案 |
autocomplete="one-time-code" 是现代表单的「神器」——
iOS Safari 会自动读取短信里的验证码填进来,配合 inputmode="numeric"
直接弹数字键盘,整个验证流程零手动。
PC 端虽然无效,但加上不亏。六、表单的可访问性收官
08 课讲了无障碍的基础(语义化、alt、键盘可达)。表单的可访问性三大件:
每个控件都有 label、错误信息用 aria-describedby 关联、
必填字段用 aria-required="true" 而不是仅靠 required。
| ARIA 属性 | 作用 | 典型用法 |
|---|---|---|
| aria-label="..." | 给控件一个无障碍名字(无法用 label 时) | 搜索图标按钮:aria-label="搜索" |
| aria-labelledby="id" | 指向页面里某段文字作为控件名 | 标题已存在时复用 |
| aria-describedby="id" | 指向错误提示 / 格式说明的 id | 错误信息、密码强度提示 |
| aria-required="true" | 读屏会念「必填」,与 required 互补 | 兼容性更好 |
| aria-invalid="true" | 校验失败时手动标记 | JS 校验失败后写上 |
时不用 aria-label(双名冲突);
控件是图标按钮等无文字的才用 aria-label;
错误信息必须用 aria-describedby 关联,读屏才会念出来。七、提交按钮与 formaction / formmethod
一个表单、三个按钮、提交到三个不同 URL——这种事 HTML 原生就能做。
formaction 让单个按钮覆盖所在 form 的 action,
formnovalidate 让单个按钮跳过校验(草稿按钮用)。
| 属性 | 作用 | 典型场景 |
|---|---|---|
| button type="submit" | 提交所在 form(默认行为) | 主提交按钮 |
| button type="reset" | 清空表单回到初始值 | 「重填」按钮(慎用) |
| button type="button" | 普通按钮,无副作用 | JS 自定义行为、关闭弹窗 |
| formaction="URL" | 本按钮提交到指定 URL(覆盖 form 的 action) | 登录 / 注册 / 找回密码三按钮一表单 |
| formmethod="get/post" | 本按钮的提交方式(覆盖 form 的 method) | 同一个表单既能 GET 也能 POST |
| formnovalidate | 本按钮跳过浏览器原生校验 | 「保存草稿」按钮 |
| formenctype | 本按钮的编码方式 | 上传文件时切 multipart/form-data |
八、本章小结
- fieldset + legend 是表单的分组卡;
fieldset[disabled]一键禁用全组;legend { float: left }做出「卡顶标签」效果。 - :user-invalid / :user-valid 比 :invalid 友好——只有用户真正改过字段才上色, 现代表单字段级反馈首选。
- placeholder 是示例不是 label——label 始终可见,placeholder 只补个例子。
- 文件上传 HTML 部分能做:
accept限定类型、multiple多选、capture调起摄像头;拖拽预览属于 JS 范围。 inputmode决定移动端键盘;autocomplete="one-time-code"+autocapitalize="off"是验证码标配。- 表单无障碍三件套:label / aria-describedby / aria-required, 错误信息必须能被读屏念出来。
formaction让单按钮覆盖 form 提交地址;formnovalidate让草稿按钮跳过校验。
把它分成「账号 / 密码 / 偏好」三组,
legend 浮到边框上当卡顶标签;② 把 :invalid 换成 :user-invalid
让错误提示更人性化;③ 给头像上传加 accept="image/*" 限定图、加 multiple 允许多选。
完成后再回头看 05/09 两课的注册表单,会发现 HTML5 已经能扛起 80% 的活。