> ## 文章索引
> 在这里获取全部文章的索引：https://lrd.im/blog/llms.txt
> 这是 lrd.im 的个人博客，记录设计、技术与思考。

# App 表单校验时机探索

![封面图](https://lrdim.oss-cn-shenzhen.aliyuncs.com/blogimg/2022-08-17-f2c1f5bfbb8d/01.png)

## 动机

在我接手 App 这波需求迭代的时候，发现一个很常见的问题却没有在设计规范里提到过（其实原本也没啥设计规范…）。那就是：表单的校验时机。

工作中我发现每个保存到数据库的表单都会有至少一个校验规则，最常见的是字数，其他的有类似格式（邮箱、数量）、上下限（价格、库存数）等。而表单的校验时机通常是在三种方式内选择：

1. 输入中校验；
2. 失焦后校验；
3. 提交时校验。

由于接手时并没有规范或者指引帮助我如何判断使用哪种，工期紧张也没有足够时间去探索，我只能拍脑袋地大部分都采取了「失焦后校验」的方法。

后面我发现这其中的差异还是挺耐人寻味的，有必要探索三者之间的各自优劣势和适用场景，于是乎就有了这篇文章。

## 三种校验时机的差异

### 输入中校验

![输入中校验示例：标签输入框中输入「123#456」，触发红色错误提示「请移除特殊符号」，键盘仍展开，用户可即时修正。](https://lrdim.oss-cn-shenzhen.aliyuncs.com/blogimg/2022-08-17-f2c1f5bfbb8d/02.png)

**效果**：输入过程中（即文本有更改时）校验并反馈报错文案。
**优点**：实时反馈，出错后立即提示。
**缺点**：容易误判。即未完成输入就提示出错。

**适用场景**：
 1. 纯前端能满足校验需求时；
 2. 校验规则较严苛时，如一个表单多条校验规则，或中途某个错误字符直接影响到表单提交时。

**修正错误的成本：低 ★**
在手机的的「键盘控件」内重新键入内容即可完成修正。

### 失焦后校验

![失焦后校验的效果演示：输入框中填入含特殊符号的 "123#456" 后，失去焦点时触发校验，输入框底部红线高亮并显示错误提示 "请移除特殊符号"。](https://lrdim.oss-cn-shenzhen.aliyuncs.com/blogimg/2022-08-17-f2c1f5bfbb8d/03.png)

**效果**：失去焦点后校验并反馈报错文案。
**优点**：输入过程中无干扰，在输入完成后再反馈报错文案。
**缺点**：失焦后才知道结果，修正成本高。

**适用场景**：
 1. 纯前端能满足校验需求时；
 2. 用户对输入框内容格式有预期时，如邮箱、手机号等。

**修正错误的成本：高 ★★**
需要先「失焦」，再回到原本的输入框「重新聚焦」，并通过「键盘控件」修正。

### 提交时检验

![提交时校验的 App 界面截图：输入框内含特殊符号「123#456」，提交后触发红色报错文案「请移除特殊符号」，底部说明提交时校验通常配合 Toast 轻提示使用。](https://lrdim.oss-cn-shenzhen.aliyuncs.com/blogimg/2022-08-17-f2c1f5bfbb8d/04.png)

**效果**：提交表单时校验并反馈报错文案，通常会伴随着使用 Toast 提示。
**优点**：后端校验时，不影响性能&体验。
**缺点**：报错反馈很不及时。

**适用场景**：
 1. 大部分需要后端校验的表单。

**修正错误的成本：较高 ★★★**
需要先「提交表单」，再「找到」出错的的输入框，并「重新聚焦」原本的输入框，通过「键盘控件」修正。

至此，上面列出了三种校验方式的各自优劣势，对比后不难得出结论。

## 结论

![推荐实时校验：输入框中输入「123#456」，系统在输入过程中实时校验并立即显示红色报错「请移除特殊符号」，展示输入中校验的即时反馈效果。](https://lrdim.oss-cn-shenzhen.aliyuncs.com/blogimg/2022-08-17-f2c1f5bfbb8d/05.png)

推荐优先使用「输入中校验」的规则。

**原因**：
 1. 反馈最及时；
 2. 修正错误的成本最低；
 3. 对研发工作量和 App 性能无影响（前端校验时）。

**理论支撑**：

> “理想情况下，所有验证都应该是内联的…”
> “…在字段完成后立即修复错误对用户的交互成本最低…”
> —— [如何反馈表单中的错误：10 条设计指南](https://www.nngroup.com/articles/errors-forms-design-guidelines/#:~:text=1.%20Aim%20for,had%20completed%20successfully.)（[Nielsen Norman Group](https://www.nngroup.com/)）

通过这次的对比，我决定了之后都将「输入中校验」作为主要的校验方式，当然这种方式也有一些不适用的场景，此时就需要换一种校验方式。举例一些常见的不适合用「输入中校验」的表单：

1. 容易误判的表单，如校验邮箱、电话、网址的格式时；
2. 设置了数量下限时，如公司业务里的 B2B 商品设置了起批量时；
3. 需要后端校验时，如需查重或对比数量上下限时。

## 其他感悟

### 对需要后端检验的表单态度

因为涉及到后端检验，意味着需要网络，那就会出现加载中/超时/加载失败/加载成功 的情况，所以这种表单一般是失焦后或提交时检验，这对输入体验会相对友好一些。

但也有例外，比如 Twitter 账号在重命名时，就使用的是输入中校验。可能是因为 Twitter 用户量庞大，特别容易重名。提交后检验反而效率更低。

![Twitter 更新用户名界面的两种状态对比：左图显示输入「@Pudge」时呈加载中状态，右图显示校验完成后出现红色错误提示「该用户名已被占用。请另选一个。」，体现输入中实时后端校验的交互方式。](https://lrdim.oss-cn-shenzhen.aliyuncs.com/blogimg/2022-08-17-f2c1f5bfbb8d/06.png)

### 输入前的体验设计

常言道好的体验设计能减少用户出错，在表单输入之前，实际上我们也可以用多种方式明示暗示文本框的填写规则。

#### 一、输入框长度

![收货地址表单示例：地址输入框横跨全宽，邮编与手机号输入框各占约半宽并排排列，通过输入框宽度差异暗示预期填写内容的长短。](https://lrdim.oss-cn-shenzhen.aliyuncs.com/blogimg/2022-08-17-f2c1f5bfbb8d/07.png)

输入框长度能过暗示文本框的预期填入内容。比如在填地址和邮编时，通常地址我们会预留比较宽的输入框给用户填写，而邮编则可以相应缩减，因为这两个类型的字段，预期填写的文本长度是有明显不同的。

参考资料：[整齐划一？不如错落有致。| Ant Design 4.0 系列分享](https://zhuanlan.zhihu.com/p/110096160)

#### 二、实时字数显示

![订单备注输入框的字数提示截图，标签显示「96/150」，展示了实时字数统计功能的典型 UI 设计样式。](https://lrdim.oss-cn-shenzhen.aliyuncs.com/blogimg/2022-08-17-f2c1f5bfbb8d/08.png)

在输入框旁边实时展示当前字数和上限是也是比较常见的做法。
**优点**：避免出错；让用户对表单的规则有一个预期。
**缺点**：页面出现过多此类提示会使页面臃肿，反而会增加视觉&认知负荷。

所以我对这个做法的态度是：每个表单都会有文字输入的上限，超过上限时也一定会禁止提交、出现提示。但是否将字数提示常驻展示，取决于 **「用户对长度是否有预期」**。

![一个表单界面截图，包含「名」和「姓」两个宽度不同的输入框，以及一个带有字数提示「备注 (0/150)」的文本域，示例说明备注类字段需常驻展示字数上限，而姓名类字段则无需展示。](https://lrdim.oss-cn-shenzhen.aliyuncs.com/blogimg/2022-08-17-f2c1f5bfbb8d/09.png)

比如在一些备注、描述、说明等大段文字里，用户可能会输入到大段文本，但又对这些输入框的上限没有预期，那我这里判断到是需要出现实时字数提示的。而像比如填写姓名、添加标签这种，字数上限只是一个兜底的判断逻辑，不需要特意暴露出来。

另外可以根据存量数据来决定是否展示实时字数提示。比如让后端同事帮忙导出在数据库里的数据，能知道用户在这个输入框里一般会填写多少个字，如果大部分情况都是接近字数上限的，意味着用户在这个输入框会输入较多的文本，此时就需要展示当前字数上限，甚至或者调整校验规则。这是我在上一家公司（千聊）里做过的事情。

#### 三、占位符（Placeholder）

![添加客户弹窗的界面设计示例：顶部为标题 "添加客户"，下方是带搜索图标的输入框，占位符文字为 "搜索客户名称、邮箱"，输入框下方展示候选列表项，展示了占位符与标签协同使用的合理布局。](https://lrdim.oss-cn-shenzhen.aliyuncs.com/blogimg/2022-08-17-f2c1f5bfbb8d/10.png)

无论是 [NNGROUP](https://www.nngroup.com/articles/form-design-placeholders/) 还是 [Shopify UX](https://polaris.shopify.com/foundations/patterns/text-fields)，都对占位符文本持有比较谨慎的态度，甚至会用到 Harmful、Avoid 等贬义词。他们主要批判的是用占位符代替标签的做法，我们在使用时避免这种用法就好了。

![反例示意图：表单仅使用占位符文本作为字段标签，无独立标签，地址、邮编、手机号三个输入框均以占位符代替标签，旁有警示图标提示此为不推荐的设计做法](https://lrdim.oss-cn-shenzhen.aliyuncs.com/blogimg/2022-08-17-f2c1f5bfbb8d/11.png)

实际上占位符和标签共同使用时没什么毛病，占位符确实能起到一定的补充作用，用来提示要输入内容的类型和名称，只是不要用来展示重要内容和代替掉标签就好了。合理使用也是减少用户出错的方式之一。

#### 四、常驻帮助文本（Help text）

![订单备注输入框示例：标题「订单备注 (12/150)」显示当前字数与上限，内容为「该客户对运费意见比较大。」，下方灰色提示文本说明「只有商家才能看到备注内容」，展示帮助文本的实际应用效果。](https://lrdim.oss-cn-shenzhen.aliyuncs.com/blogimg/2022-08-17-f2c1f5bfbb8d/12.png)

帮助文本可以视为占位符的进阶版，具体效果在输入框附近常驻一段简短、必要的说明内容，帮助商家了解输入框所要求的格式，或输入后的内容会怎么处理。甚至还可以链接到 FAQ，有丰富的用法。

## 一条原则

在文本有上限的输入框里，我们会面临一个选择是：超过字数后是否允许输入？

![两个文本输入框对比：上方输入框内容未超限（"孤蓬" 后有光标），状态正常；下方输入框内容超出限制，底部红线报错，提示 "订单备注不能超过 20 个字"，同时允许用户继续输入。](https://lrdim.oss-cn-shenzhen.aliyuncs.com/blogimg/2022-08-17-f2c1f5bfbb8d/13.png)

我在这里的建议是允许继续输入，同时会出现报错的反馈，告知规则。因为翻查了很多 UX 资料，都建议在设计中要避免「打断了用户行为」。下面放出两种方法的优劣对比，各位看官理性抉择：

| 方案 | 效果 | 优点 | 缺点 |
|------|------|------|------|
| 超上限后允许继续输入 | 超出上限时直接出现报错反馈。同时允许继续输入文本 | 反馈及时，原因清晰 | 要做好用户直接提交非法内容时的兜底处理，设计和前端工作量都会轻微增加 |
| 到达上限后禁止输入 | 禁止输入超过上限后的文字 | 简单直接，实现成本最低 | 没有明确反馈，用户可能会不知道错误原因 |

这么看下来，就体验而言是「超出上限后允许继续输入」要好很多。而且这种方法还照顾到一个场景是：用户在输入中或粘贴一段文本并发现超出长度后，可以主动删减其他内容。

当然毫无疑问，到达上限后直接不允许输入是对设计和研发来说最省力的做法。这种做法下不用反馈，也就不需要做反馈时机的决策、反馈的文案及多语言、文本的适配、反馈后的布局适配…

我有一段经历是刚介入到项目当中，工期紧张且缺少规范。当时有很多比制定文本输入规则还重要的事情需要处理，所以就先都用了「到达上限后禁止输入」的方法。

后面与其他方式对比发现这种做法应该是体验比较差的做法了。像现在我把规范做法加进 App 设计的指引里面之后，就大多数情况下都会使用「允许继续输入，在输入中反馈」的方式实现。

## 结语

以上内容就是目前来说我对文本框校验规则的一些认识，包含了各种校验时机的对比，输入前的体验设计，以及不打断用户操作的原则。

这次的总结是我挺久之前就想干的事情了，因为平常工作中一直遇到这种问题，也没有一个明确的设计指引能够参考，现在自己写下来这篇笔记之后，之后的设计方案会考虑得更周全，说服力也更强了（希望是吧）…

同时再浅挖一个坑：之后要探索下表单提交按钮相关知识，比如说什么时候要禁用按钮，什么时候是允许点击但报错等等，这个应该还要复杂一点…

## 参考链接

- [10 Design Guidelines for Reporting Errors in Forms - NN/G](https://www.nngroup.com/articles/errors-forms-design-guidelines/)
- [整齐划一？不如错落有致。| Ant Design 4.0 系列分享](https://zhuanlan.zhihu.com/p/110096160)
- [Placeholders in Form Fields Are Harmful - NN/G](https://www.nngroup.com/articles/form-design-placeholders/)
- [Text fields — Shopify Polaris React](https://polaris.shopify.com/foundations/patterns/text-fields)