表单法典:每个字段都是一个问题
表单,是界面停止展示、开始发问的那一刻。每个字段都是抛给一个本想去做别的事的人的问题:多余的字段是一种强加,含混的字段则是一次小小的背叛。表单的手艺可以浓缩成一部随时装在脑子里的法典——问得更少,排成一栏,标签始终可见;按人们自然作答的方式接受答案;等用户说完一个念头再校验,而不是打字打到一半就动手;错误提示要写清怎么修;以及无论如何,绝不丢弃用户已经输入的内容。现实中绝大多数表单摩擦,都是这几条里某一条被违反的结果。 {.answer-block}
TL;DR
- 表单是一场对话,所以要像人一样发问: 问题尽可能少,排成一栏依次推进,按主题分组——每个字段都得扛得住“发问的代价”这一质问。
- 布局早有定论: 单栏、标签置于字段上方、字段宽度暗示答案的长度。多栏表单和浮动标签这类花招,是拿真实的理解力去换想象中的精致。
- 按人们给出答案的方式接受答案。 顺手去掉多余的空格,电话号码什么格式都收,把一个字段留作整体而不是拆成三个输入框——规范化是软件的活儿,不是用户的活儿。
- 在字段失去焦点时校验,而不是每敲一个键就校验;错误提示要能教人修复: 哪里出了错、怎么改,就写在出问题的那个字段旁边。
- 用户输入的内容是神圣的。 提交失败就清空表单,或者摆一个不肯说明理由的禁用按钮,会把一位乐意配合的参与者变成前参与者。
为什么每个字段都是一个问题
把表单当成一次访谈的逐字稿,它的质量立刻就变得可读。一位称职的访谈者会出于习惯问你的传真号码吗?会在不说明理由的情况下要你交出出生日期吗?会在你拼写邮箱拼到一半时打断你,宣布它无效吗?会因为你的邮编里多了个空格,就把你说过的一切统统忘掉吗?这些做法在真实上线的表单里全都有对应版本,而用户对它们的感受,与面对那位访谈者时一模一样:无礼。
这个视角也直接给出了第一条、同时也是最重要的一条规则,它先于一切布局与样式:每个字段都必须以“发问的代价”为标尺,证明自己存在的必要。 每多问一个问题,放弃率就上升一分;每收集一个答案,就多一份需要存储、保护并为之负责的数据。表单设计中威力最大的动作是删除——被你删掉的那个字段,胜过你可能施加在它身上的任何打磨。最经典的凭证来自 Expedia:把预订表单里“Company”这一个可选字段删掉,据报道每年价值约 1200 万美元——此前有客户在那里填了开户银行的名称,结果地址验证失败。一个字段,经过诚实的拷问,就赢过了任何一次重设计。“可选”不构成理由;它只是较轻的强加,终究还是强加。只问完成这笔交易所必需的内容,把那少数真正可选的字段标注为可选,其余一律推迟到关系足够牢固之后再说。(对立阵营的做法是给每个必填字段加星号;当几乎一切都必填时,星号就成了墙纸。)
布局规则
表单布局是设计中少数几个证据基本已成定论的角落,因此偏离它就等于主动选择与用户为敌:
单栏。 表单是一串依次抛出的问题,而单栏让这个顺序毫无歧义:作答、下移、完成。多栏布局在每一行都逼用户做一次阅读顺序的判断——横着走还是竖着走?——而人们的判断并不一致,于是漏掉了自己压根没看见的字段。在最著名的那项眼动追踪对比中,同样的字段排成一栏,比拆成两栏大约快十五秒完成。例外是那些确实读作同一个答案的复合项:同一行上的城市/省份/邮编,分成三段的日期。它们是一个问题穿了三个输入框的外衣,而不是三个问题。
标签置于字段上方,始终可见。 标签摆在字段旁边,会让视线走出参差不齐的折线;标签塞进字段里(把占位符当标签用),则在用户开始打字的那一刻消失,而那恰恰是最需要它的时刻——长表单填到一半,每个已填字段都变成一个“这栏刚才问的是啥”的盲盒。折中方案浮动标签虽然在获得焦点后依然保留,却缩到了辅助文字的尺寸,还让空字段看起来像已填:同一笔交易的温和版本,代价依旧由理解力支付。占位符是用来给格式提示的(“[email protected]”),永远不该用来承载问题本身。
字段宽度本身就是信息。 一个和地址栏一样宽的邮编字段,是在对答案的形状撒谎。按预期内容给输入框定尺寸——邮编短、地址长——和用间距编码分组是同一门手艺:让几何形状默默传达信息。
按主题分组,并让留白来完成分组。 联系方式、配送、支付——相关问题成簇,簇与簇之间留有清晰的接缝,而接缝由留白构成,不是方框,也不是分隔线。读起来像三个小主题的表单,在心理上比同样的字段堆成一整块无差别石板要小得多。
输入规则
输入设计的主旨只有一句话:规范化是软件的活儿。 格式上的负担无论多重,都由机器来扛,因为在意格式的本来就是机器。
- 接受潦草的答案。 去掉首尾空白——自动补全的邮箱末尾那个空格,害掉的登录次数远超它应得的份额。电话号码带连字符、点、空格、括号,或者什么都不带,一律照收。卡号有没有分隔都接受。只要解析得了,就去解析;因为你想要
5558675309而拒绝555 867 5309,无异于让用户用手替你跑一遍字符串格式化代码。 - 绝不拆分用户心里视为一体的东西。 电话号码分三个输入框、日期分三个下拉菜单、验证码分成六个单字符格子外加手写的焦点跳转——每一种都把一个完整的心理答案拆成一道导航谜题,而且通常会破坏粘贴,也就是用户手上最高效的输入方式。(布局规则里的复合项例外依然成立:日期分成三段手动输入没有问题——罪过在于下拉菜单的繁文缛节和被抢走的焦点,而不是相邻本身。至于验证码,经得起时间考验的答案是一个输入框加上
autocomplete="one-time-code"。) - 召唤正确的键盘。 在触屏设备上,
type="email"、inputmode="numeric"这类属性,决定了用户是在为此而生的键盘上敲地址,还是在符号面板里层层翻找@。代价不过一个属性。 - 让浏览器帮忙。 正确的
autocomplete取值,能把十二个字段的结账流程变成老客户的两次点按。在地址和支付字段上禁用自动填充——通常只是某场从未发生过的安全评审留下的迷信——等于扔掉表单所能得到的最大一笔提速。 - 手指落在哪里,就在哪里接住它。 字段、字段上的按钮,以及任何可点按的元素,都要守住平台的最小触摸目标——iOS 上 44pt,Android 上 48dp。一个紧凑优雅、拇指却按不准的字段,只是一件穿着手机戏服的桌面表单。
键盘与自动填充这两条规则加起来,也不过几个属性的成本:
<input type="tel" autocomplete="tel"> <!-- phone keypad, autofilled -->
<input inputmode="numeric" autocomplete="one-time-code"> <!-- digit pad, code autofills -->
校验规则
校验的时机,是表单最常显露敌意的地方,而规则很简单:等用户说完一个念头再回应。 每敲一个键就触发,等于冲着一个刚打了四个字符的人大喊“邮箱无效!”——句子还没说完就先挑毛病。相反的流派只在提交时校验,而它有一位分量十足的辩护者:GOV.UK 的设计系统正是这么做的,提交的同时在页面顶部给出错误汇总,因为汇总可以播报给屏幕阅读器,也给键盘用户一个统一的修复起点。这是一个自洽的立场,专为无障碍是硬约束的服务而调校。不过对大多数产品表单,我仍然站在失焦这一边:用户填完一个字段、往下走,在两个念头的接缝处拿到反馈,而那个念头还是热的。(一处微调:已经被判定为无效的字段,可以改为每敲一键就重新校验,这样修好的瞬间红色状态就消失,而不是整整滞后一个字段。)
错误文案要过的是同一场对话测试。错误提示不是判决,而是一份修复说明。“输入无效”过不了这一关——到底哪里无效?“这个邮箱地址缺少 @”就过得了。(而且这份说明必须与真实规则相符:卡号的合法长度是 12 到 19 位,所以“必须是 16 位”不是错误提示,而是一个把所有 Amex 持卡人拒之门外的校验缺陷。)把消息放在它所指的那个字段旁边,并以文字呈现——只靠颜色会把色盲用户排除在外,而文字是一个什么都不渲染的读者唯一能看见的通道——再用程序把它与输入框绑定(aria-describedby),让辅助技术把错误连同字段一起播报,而不是任它孤零零地滞留在屏幕上。语气保持陈述事实:表单的职责是让用户顺利通过,而不是裁定谁有过错。整条规则就浓缩在下面这一对示例里:
<!-- before: a verdict, visually nearby, programmatically stranded -->
<label for="email">Email</label>
<input id="email" type="email">
<span class="error">Invalid input</span>
<!-- after: a repair instruction, announced with its field -->
<label for="email">Email</label>
<input id="email" type="email"
aria-invalid="true" aria-describedby="email-err">
<span id="email-err">This email address is missing its @</span>
还有两条结构性规则为这套规则收尾。绝不要把禁用提交按钮当成校验策略——一个不作任何解释的死按钮就是一道谜题,而用户的下一步动作是离开;让他们提交,然后精确指出哪里需要处理。(提交请求进行中为防止重复扣款而临时禁用是另一回事:那是状态,不是评判。)以及这部法典中最根本的一条:提交失败必须原样保留用户敲下的每一个字符。 出错就清空的表单,是把一个人几分钟的心血当着他的面烧掉。任何视觉上的精修都无法从这里挽回。
化作清单的法典
以上所有内容的可执行版本,任何表单上线之前都可以对照一遍:
| 规则 | 它防住的祸害 |
|---|---|
| 每个字段要么被论证,要么被删除 | 出于好奇心的提问买来的放弃率 |
| 单栏,复合项除外 | 阅读顺序含混导致的漏填 |
| 标签置于上方,始终可见 | 填到一半的盲盒;把占位符当标签 |
| 字段宽度匹配答案的形状 | 几何形状对预期输入撒谎 |
| 按主题分组,接缝由留白构成 | 无差别的一整块石板 |
| 接受任何可解析的格式 | 用户替你手工执行格式化代码 |
| 一个答案,一个输入框(复合项除外) | 粘贴被破坏;焦点跳转谜题 |
| 正确的键盘与 autocomplete | 触屏上翻找符号;本可两次点按却要十二次 |
| 遵守平台触摸目标(44pt/48dp) | 拇指按不中的优雅字段 |
| 失焦时校验;错误提示能教人修复、紧邻字段、以 aria 绑定 | 打字打到一半就被训斥;错误与辅助技术失联 |
| 绝不为校验而禁用提交 | 死按钮谜题 |
| 输入在失败之后依然存活 | 被清空的表单,以及再也不回来的用户 |
在设计系统里,这些规则会固化进表单组件本身——一个文本字段出厂时就自带上方的标签插槽、下方的错误插槽和内建的校验时机——于是法典默认成立,偏离反而需要额外的力气。这和动效 token所依据的是同一套系统化论证:要么把决定编码一次,要么在每个功能里重新争论一遍。
常见问题
表单应该用一栏还是两栏?
一栏。单栏让作答顺序毫无歧义,完成速度也有可测量的提升;多栏布局会造成漏填,因为用户对阅读顺序的判断并不一致。例外是复合答案——城市/省份/邮编——它本是一个问题,只是用相邻的几个输入框来表达。
表单校验应该在什么时候触发?
在失去焦点时——也就是用户离开某个字段时——而不是每敲一个键;对大多数产品表单来说,也不该攒到提交时一并抛出。按键校验是在对尚未说完的答案挑毛病;只在提交时校验则把所有失败一次性倾倒出来,不过在屏幕阅读器播报是硬约束的场景里,GOV.UK 那种提交时的错误汇总才是正确选择。已经被标记为无效的字段,可以改为每敲一键复查一次,这样修好后错误提示立刻消失。
可以拿占位文字当字段标签用吗?
不可以。占位符一旦被当作标签,就会在用户开始打字时消失,而那正是他需要回想问题内容的时刻;到复查时,每个已填字段都成了没有标签的数据。请在字段上方保留一个可见标签,把占位符留给格式示例。
提交按钮应该在表单校验通过前保持禁用吗?
不应该。一个不作解释的禁用提交按钮,是一条要用户自己去诊断的死路。让它保持可用,并在提交时把每个尚未解决的字段连同旁边具体而有指导性的错误提示一并暴露出来——同时保留用户已经输入的一切。