12表单高级话题

05、09 两课把表单的「骨头」搭好了:控件家族、HTML5 新属性、浏览器原生校验。 这一课往血肉方向走——fieldset / legend 把表单拆成「分组卡片」、 :user-invalid / :user-valid 让浏览器智能识别「用户修改过」的状态、 文件上传的 accept / multiple / 拖拽预览, 还有可访问性、复合按钮这一类「正经项目才用得到」的话题。

约 18 分钟 6 个动手实验 表单进阶 + 文件上传

一、fieldset 与 legend:表单的「分组卡片」

09 课简单提过

;本课把它玩出花来——它嵌套、 disabled 一键禁用组内全部控件,再配合 做出「卡顶标签」效果。 这些是设计稿里分组注册卡、信息面板的常见写法。

fieldset / legend 关键属性与玩法
标签 / 属性作用备注
把一组相关控件圈成一个分组框语义化、读屏软件会念「分组」
分组框的标题,必须是 fieldset 的第一个孩子默认渲染在边框左上角
fieldset[disabled]一键禁用组内所有表单控件含嵌套 fieldset 也会一起禁用
fieldset > legend { float: left }让 legend「浮」到边框上做成卡顶标签经典 UI 小技巧
fieldset 嵌套大分组里再分小组(两层标签)读屏会按层级逐层念
一个分三组的复杂表单:把第二个 fieldset 的 disabled 写上,看「联系偏好」整组置灰——表单里有需要「按条件启用」的场景,fieldset 一次搞定
设计师嫌 fieldset 默认样式丑?给它写 border / border-radius / padding 的重置样式,保留它的语义(读屏能识别分组),视觉外观交给设计稿。 legend { float: left } 这个小技巧让 legend 浮到边框上, 配合背景色直接变成「卡顶标签」,比纯文字标题精致得多。

二、用户态伪类 :user-invalid 与 :user-valid

09 课学过 :invalid / :valid。但它有个反人类的设计: 用户还没碰过表单,一加载就红(因为空 required 字段就是 invalid)。 HTML 给出的解药是 :user-invalid / :user-valid—— 只有用户真正修改过字段、且当前值不合法时才上色。

:invalid vs :user-invalid:触发时机差在哪
伪类触发时机用户体验推荐场景
: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 三种角色对比:用对场景才有意义
角色用法对不对原因
示例值placeholder="如 zhang@example.com"✓ 推荐有 label 在,placeholder 只补个例子
格式提示placeholder="11 位手机号,1 开头"✓ 可用补充说明字段格式
当 labelplaceholder="请输入邮箱"(无 label)✗ 反模式对比度差、信息会消失、无障碍灾难
左边 placeholder 当 label(错:用户输入后没有视觉线索)、右边 label 在外 + placeholder 当示例(对:标签始终可见)
:placeholder-shown 用来「当字段为空时」的样式钩子—— 比如「输入框为空时显示一个图标、开始输入就隐藏」。 它判断的是 placeholder 文本是否还在展示,不是字段是否有值。

四、文件上传:accept / multiple / 拖拽

是表单里唯一一类必须靠 JS 才能玩出花的控件—— 浏览器负责选文件,JS 负责「预览 / 上传 / 拖拽 / 进度」。本节只看 HTML 部分能做的事。

file input 关键属性:accept / multiple / capture
属性作用示例
accept限定文件类型,文件选择器自动过滤accept="image/*" 只显示图片
accept 细分用 MIME 或扩展名精确到具体格式accept=".jpg,.png,image/jpeg"
multiple允许多选(按住 Ctrl / Cmd)
capture移动端直接调起摄像头 / 麦克风capture="environment" 后置摄像头
required必填,提交时校验至少有一个文件配合 multiple 时校验总有效文件 ≥1
三种 accept 过滤对比:把第三个 accept 改成 "video/*",看文件选择器从「图」切到「视频」过滤;加 multiple 看选择器支持多选
沙箱硬约束:本课不写 onclick、不嵌 JS。 拖拽预览、即时缩略图、上传进度这类功能必须 JS 介入—— 它们属于 HTML 之后的「JavaScript 篇章」。但请记住:HTML 部分能做的 就已经做了(限定类型 / 多选 / 必填),JS 只是把这些体验串起来。

五、inputmode 与 autocomplete 进阶

移动端表单体验好不好,键盘弹什么是关键。 inputmode 决定虚拟键盘样式,autocomplete 决定浏览器是否自动填、 填什么类型的内容,autocapitalize 决定是否首字母大写。

inputmode 七值:移动端虚拟键盘怎么弹
inputmode键盘适用字段
text普通键盘(默认)用户名、地址、备注
numeric纯数字键盘验证码、数量
decimal数字 + 小数点价格、金额
tel拨号键盘(带 * #)电话号码
email英文键盘 + @ 键邮箱地址
url英文键盘 + / .com 键网址
search键盘右下角是「搜索」按钮搜索框
autocomplete 常用值:让浏览器填对的内容
autocomplete含义典型场景
on / off开 / 关浏览器记忆通用开关
name填全名收货人、注册姓名
email填邮箱登录邮箱、联系邮箱
current-password填当前密码登录、改密
new-password填新密码(不记忆)注册、改密
one-time-code填短信验证码两步验证、找回密码
tel / tel-national填电话收货电话
off明确关闭记忆一次性邀请码、测试答案
四个输入框分别配 inputmode 和 autocomplete:手机扫码打开这个页面点「验证码」框,会自动弹数字键盘(仅手机可见)
autocomplete="one-time-code" 是现代表单的「神器」—— iOS Safari 会自动读取短信里的验证码填进来,配合 inputmode="numeric" 直接弹数字键盘,整个验证流程零手动。 PC 端虽然无效,但加上不亏。

六、表单的可访问性收官

08 课讲了无障碍的基础(语义化、alt、键盘可达)。表单的可访问性三大件: 每个控件都有 label、错误信息用 aria-describedby 关联、 必填字段用 aria-required="true" 而不是仅靠 required。

表单无障碍三件套:label / aria-describedby / aria-required
ARIA 属性作用典型用法
aria-label="..."给控件一个无障碍名字(无法用 label 时)搜索图标按钮:aria-label="搜索"
aria-labelledby="id"指向页面里某段文字作为控件名标题已存在时复用
aria-describedby="id"指向错误提示 / 格式说明的 id错误信息、密码强度提示
aria-required="true"读屏会念「必填」,与 required 互补兼容性更好
aria-invalid="true"校验失败时手动标记JS 校验失败后写上
无障碍对照:左边只写 placeholder(差:读屏只念「输入框」)、右边 label + aria-describedby + aria-required(好:读屏念「必填·邮箱·输入框·需符合邮箱格式」)
三个 ARIA 属性怎么搭配: 控件有可见 时不用 aria-label(双名冲突); 控件是图标按钮等无文字的才用 aria-label; 错误信息必须用 aria-describedby 关联,读屏才会念出来。

七、提交按钮与 formaction / formmethod

一个表单、三个按钮、提交到三个不同 URL——这种事 HTML 原生就能做。 formaction 让单个按钮覆盖所在 form 的 action, formnovalidate 让单个按钮跳过校验(草稿按钮用)。

button 与 form 提交相关属性
属性作用典型场景
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
一个登录 form 三按钮:点「登录」提交到 /login、「注册」跳到 /signup、「忘记密码」带 formnovalidate 直接提交——三个按钮共享同一个表单
草稿按钮必加 formnovalidate—— 否则用户填到一半点「保存草稿」也会被 required 拦截,已经填的内容反而丢了。 同理「忘记密码」按钮也常加,跳转到找回页时不必先校验登录表单。

八、本章小结

  • 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% 的活。