表单设计模式书籍摘录:注册表单
已发表: 2022-03-10让我们从注册表开始。 大多数公司都希望与用户建立长期关系。 为此,他们需要用户注册。 为此,他们需要为用户提供价值作为回报。 没有人想注册你的服务——他们只想访问你提供的任何东西,或者他们下次访问时提供更快体验的承诺。
尽管注册表具有基本外观,但仍有许多事情需要考虑:构成表单的原始元素(标签、按钮和输入)、减少工作量的方法(即使是这样的小表单),一直到表单验证。
在选择这样一个简单的形式时,我们可以放大在精心设计的形式中发现的基本品质。
## 它可能看起来如何该表单由四个字段和一个提交按钮组成。 每个字段都由一个控件(输入)及其关联的标签组成。

这是HTML:
<form> <label for="firstName">First name</label> <input type="text" name="firstName"> <label for="lastName">Last name</label> <input type="text" name="lastName"> <label for="email">Email address</label> <input type="email" name="email"> <label for="password">Create password</label> <input type="password" name="password" placeholder="Must be at least 8 characters"> <input type="submit" value="Register"> </form>
标签是我们讨论的开始。
## 标签在Accessibility For Everyone中,Laura Kalbag 列出了改善每个人的用户体验的四大参数:
- 视觉:使其易于看到。
- 听觉:使其易于听到。
- 电机:使其易于交互。
- 认知:使其易于理解。
通过从每个角度查看标签,我们可以看到标签的重要性。 有视力的用户可以阅读它们,视力受损的用户可以使用屏幕阅读器听到它们,而运动受损的用户可以更容易地将焦点集中在该领域,这要归功于更大的命中区域。 这是因为单击标签会将焦点设置到关联的表单元素。

由于这些原因,每个接受输入的控件都应该有一个辅助<label>
。 提交按钮不接受输入,因此它们不需要辅助标签 - 呈现按钮内部文本的value
属性充当可访问标签。
要将输入连接到标签,输入的id
和标签的for
属性应该匹配并且对于页面是唯一的。 在 email 字段的情况下,值为“email”:
html < label for = "email" > Email address </ label > < input id = "email" >
不包含标签意味着忽略许多用户的需求,包括那些有身体和认知障碍的用户。 通过关注对残疾人的公认障碍,我们可以使我们的表格更容易和更强大,适合每个人。
例如,较大的击球区域对于运动障碍用户来说至关重要,但对于没有障碍的用户来说也更容易击球。
## 占位符placeholder
属性用于存储提示。 它在填写字段时为用户提供了额外的指导 - 对于具有复杂规则的字段(例如密码字段)特别有用。
由于占位符文本不是真正的值,因此它显示为灰色,以便与用户输入的值区分开来。

与标签不同,提示是可选的,不应该理所当然地使用。 仅仅因为placeholder
属性存在并不意味着我们必须使用它。 当标签是“名字”时,您不需要“输入您的名字”的占位符——这是不必要的重复。

占位符很有吸引力,因为它们具有最小的、节省空间的美感。 这是因为占位符文本放置在字段内。 但这是给用户提示的一种有问题的方式。
首先,它们会在用户键入时消失。 消失的文本很难记住,这可能会导致错误,例如,如果用户忘记满足密码规则之一。 用户经常将占位符文本误认为是一个值,从而导致该字段被跳过,这又会在以后导致错误。 灰白色文本缺乏足够的对比度,通常难以阅读。 最重要的是,一些浏览器不支持占位符,一些屏幕阅读器不宣布它们,长提示文本可能会被截断。

对于本质上只是文本的内容来说,这是很多问题。 所有内容,尤其是表单提示,都不应该被认为是好的。 因此,与其使用占位符,不如将提示文本放置在控件上方,如下所示:

<div class="field"> <label for="password"> <span class="field-label">Password</span> <span class="field-hint">Must contain 8+ characters with at least 1 number and 1 uppercase letter.</span> </label> <input type="password" name="password"> </div>
提示放置在标签内和<span>
内,因此可以采用不同的样式。 通过将其放置在标签内,屏幕阅读器将读取它,并进一步扩大点击区域。
与设计中的大多数事情一样,这不是实现此功能的唯一方法。 我们可以使用 ARIA 属性将提示与输入相关联:
<div class="field"> <label for="password">Password</label> <p class="field-hint">Must contain 8+ characters with at least 1 number and 1 uppercase letter.</p> <input type="password" name="password"> </div>
aria-describedby
属性用于通过其id
连接提示 - 就像标签的 for 属性一样,但相反。 它被附加到控件的标签上,并在短暂的停顿后读出。 在此示例中,“密码 [pause] 必须包含八个加字符,其中至少有一个数字和一个大写字母。”
还有其他差异。 首先,单击提示(在本例中为<p>
)不会使控件聚焦,从而减少了点击区域。 其次,尽管 ARIA 的支持越来越多,但它永远不会像原生元素那样受到支持。 在这种特殊情况下,Internet Explorer 11 不支持aria-describedby
。 这就是为什么 ARIA 的第一条规则是不要使用 ARIA:
“如果您可以使用原生 HTML 元素或属性以及您需要的已内置语义和行为,而不是重新利用元素并添加 ARIA 角色、状态或属性以使其可访问,那么就这样做。”
浮动标签
Matt Smith 的浮动标签模式是一种使用标签作为占位符的技术。 标签从控件内部开始,但在用户键入时浮动在控件上方,因此得名。 这种技术经常因其古怪、简约和节省空间的品质而受到称赞。

不幸的是,这种方法存在几个问题。 首先,没有提示空间,因为标签和提示是相同的。 其次,它们很难阅读,因为它们的对比度很差并且文字很小,因为它们通常是设计的。 (较低的对比度是必要的,这样用户才有机会区分实际值和占位符。)第三,与占位符一样,它们可能会被误认为是值并可能被裁剪。
浮动标签实际上并没有节省空间。 标签首先需要空间才能移动。 即使它们确实节省了空间,这也不是降低表单可用性的好理由。
“当你可以简单地将标签放在输入之上并获得所有好处/没有任何问题时,这似乎需要付出很多努力。”
— Luke Wroblewski 谈浮动标签
古怪和简约的界面不会让用户感觉很棒——明显、包容和健壮的界面会。 像这样人为地降低表格的高度既不引人注目,也有问题。
相反,您应该在设计过程开始时优先考虑为始终存在、随时可用的标签(并在必要时提示)腾出空间。 这样您就不必将内容压缩到狭小的空间中。
我们将很快讨论几种不那么人工的技术来减小表单的大小。
## 问题协议减小表单大小的一种强大而自然的方法是使用问题协议。 它有助于确保您知道为什么要问每个问题或包含表单字段。
注册表需要收集名字,姓氏,电子邮件地址和密码吗? 是否有更好或替代的方式来询问这些信息以简化体验?
很有可能,您无需询问用户的姓名即可注册。 如果您以后需要该信息,无论出于何种原因,请在那时索取。 通过删除这些字段,我们可以将表单的大小减半。 所有这些都没有诉诸新奇和有问题的模式。
### 无密码登录避免要求用户输入密码的一种方法是使用无密码登录模式。 它通过利用电子邮件的安全性(已经需要密码)来工作。 用户只需输入他们的电子邮件地址,该服务就会向他们的收件箱发送一个特殊链接。 在它之后,用户会立即登录到服务中。

这不仅将表单的大小减少到只有一个字段,而且还让用户不必记住另一个密码。 虽然这单独简化了表单,但在其他方面它为用户增加了一些额外的复杂性。
首先,用户可能不太熟悉这种方法,许多人担心在线安全。 其次,必须从应用程序转移到您的电子邮件帐户是冗长的,特别是对于知道密码或使用密码管理器的用户而言。
这并不是说一种技术总是比另一种更好。 问题协议敦促我们将其视为设计过程的一部分。 否则,您会盲目地在表单上添加一个密码字段并完成它。
### 密码短语密码通常很短,很难记住,也很容易破解。 用户通常必须创建超过八个字符的密码,至少由一个大写字母和一个小写字母以及一个数字组成。 这种微交互并不理想。
“抱歉,您的密码必须包含大写字母、数字、俳句、帮派标志、象形文字和处女血。”
— 匿名网络模因
我们可以要求用户输入密码,而不是密码。 密码短语是一系列单词,例如“monkeysinmygarden”(对不起,这是首先想到的)。 它们通常比密码更容易记住,并且由于它们的长度而更安全——密码必须至少有 16 个字符长。
缺点是密码短语不太常用,因此不熟悉。 这可能会让已经担心在线安全的用户感到焦虑。
无论是无密码登录模式还是密码短语,只有在我们进行了彻底和多样化的用户研究之后,我们才应该打破常规。 您不想在不知不觉中将一组问题换成另一组问题。
## 字段样式您设计表单组件的方式至少部分取决于您的产品或公司的品牌。 尽管如此,标签位置和焦点样式仍然是重要的考虑因素。
### 标签位置Matteo Penzo 的眼球追踪测试表明,将标签放置在表单控件上方(而不是旁边)效果最好。
“在其输入字段上放置一个标签允许用户通过一次眼球运动来捕捉这两个元素。”
但是还有其他原因将标签放在字段上方。 在小视口上,控件旁边没有空间。 在大视口上,放大会增加文本从屏幕上消失的机会。
此外,一些标签包含大量文本,这会导致它换行成多行,如果放在控件旁边会破坏视觉节奏。
虽然您的目标应该是保持标签简洁,但这并不总是可能的。 使用适应不同内容的模式——通过在控件上方放置标签——是一个很好的策略。
### 外观、大小和空间表单域应该看起来像表单域。 但这究竟是什么意思?
这意味着文本框应该看起来像一个文本框。 空盒子由于是空的,就意味着“填满我”,就像一本涂色书。 这恰好是占位符无用的部分原因。 他们消除了空文本框原本会提供的感知可供性。
这也意味着空白区域应该被装箱(加边框)。 例如,移除边框或仅具有底部边框会移除感知到的可供性。 底部边框起初可能看起来是一个分隔符。 即使您知道必须填写某些内容,该值是高于还是低于该线?
在空间上,标签应该最接近其表单控件,而不是前一个字段的控件。 看起来很接近的事物表明它们属于同一类。 具有相等的间距可能会提高美感,但会以可用性为代价。
最后,标签和文本框本身应该足够大以便阅读和点击。 这可能意味着字体大小至少为 16 像素,理想情况下,整体点击目标至少为 44 像素。
### 焦点样式焦点风格是一个更简单的前景。 默认情况下,浏览器会在焦点元素周围放置一个轮廓,以便用户,尤其是那些使用键盘的用户,知道他们在哪里。 默认样式的问题在于它通常模糊且难以看到,而且有些难看。
在这种情况下,不要试图删除它,因为这样做会大大降低那些通过键盘遍历屏幕的用户体验。 我们可以覆盖默认样式,使其更清晰、更美观。
input:focus { outline: 4px solid #ffbf47; }
## 电子邮件字段尽管它的外观很简单,但该领域的构造中仍有一些重要的细节会影响体验。

如前所述,一些字段除了标签之外还有提示,这就是标签位于子跨度内的原因。 field-label
类允许我们通过 CSS 对其进行样式设置。
<div class="field"> <label for="email"> <span class="field-label">Email address</span> </label> <input type="email" name="email"> </div>
标签本身是“电子邮件地址”并使用句子大小写。 在“为字母大小写做一个案例”中,John Saito 解释说,句子大小写(相对于标题大小写)通常更容易阅读、更友好,并且更容易识别名词。 你是否听从这个建议取决于你,但无论你选择什么风格,一定要始终如一地使用它。
输入的type
属性设置为email
,这会在移动设备上触发特定于电子邮件的屏幕键盘。 这使用户可以轻松访问@
和.
每个电子邮件地址必须包含的(点)符号。

使用不支持浏览器的人将看到标准文本输入 ( <input type="text">
)。 这是一种渐进增强的形式,是设计包容性体验的基石。
渐进式增强是关于用户的。 它恰好也让我们作为设计师和开发人员的生活更轻松。 我们可以只关注功能,而不是跟上一组浏览器和设备(这是不可能的!)。
首先,渐进增强是始终为用户提供合理的体验,无论他们的浏览器、设备或连接质量如何。 当事情出错时——他们会——用户不会因为他们仍然可以完成事情而受苦。
体验出错的方式有很多。 也许样式表或脚本无法加载。 也许一切都加载了,但用户的浏览器无法识别某些 HTML、CSS 或 JavaScript。 不管发生什么,在设计体验时使用渐进增强可以让用户度过一段特别糟糕的时光。
它从结构和内容的 HTML 开始。 如果 CSS 或 JavaScript 没有加载,那很好,因为内容在那里。
如果一切正常,则可能无法识别各种 HTML 元素。 例如,一些浏览器不理解<input type="email">
。 不过,这很好,因为用户会得到一个文本框( <input type="text">
)。 用户仍然可以输入电子邮件地址; 他们只是没有在移动设备上获得特定于电子邮件的键盘。
也许浏览器不理解一些花哨的 CSS,它会忽略它。 在大多数情况下,这不是问题。 假设您有一个带有border-radius: 10px
的按钮。 无法识别此规则的浏览器将显示一个带有角的按钮。 可以说,按钮的感知功能降低了,但用户没有受到伤害。 在其他情况下,使用特征查询可能会有所帮助。
然后是JavaScript,它更复杂。 当浏览器尝试解析它无法识别的方法时,它会发出嘶嘶声。 这可能会导致您的其他(有效且受支持的)脚本失败。 如果您的脚本在使用它们之前没有首先检查方法是否存在(特征检测)和工作(特征测试),那么用户可能会得到一个损坏的界面。 例如,如果按钮的单击处理程序调用了无法识别的方法,则该按钮将不起作用。 那很糟。
这就是你如何增强。 但更好的是根本不需要增强。 带有一点 CSS 的 HTML 可以为用户提供出色的体验。 重要的是内容,您不需要 JavaScript。 您越能依赖内容 (HTML) 和样式 (CSS),就越好。 这一点我怎么强调都不为过:很多时候,基本体验是最好和最高效的体验。 如果某些东西不能增加价值,那么增强它是没有意义的(参见包容性设计原则 7)。
当然,有时基本体验不如预期的那么好——那就是提升的时候了。 但是如果我们按照上面的方法,当一段 CSS 或 JavaScript 没有被识别或执行时,事情仍然会起作用。
渐进式增强让我们思考当事情失败时会发生什么。 它让我们能够建立具有韧性的体验。但同样,它让我们思考是否需要增强; 如果是,如何最好地去做。
## 密码字段我们使用与前面讨论的电子邮件字段相同的标记。 如果您使用模板语言,您将能够创建一个包含两种类型字段的组件。 这有助于实施包容性设计原则 3,保持一致。

<div class="field"> <label for="password"> <span class="field-label">Choose password</span> <span class="field-hint">Must contain 8+ characters with at least 1 number and 1 uppercase letter.</span> </label> <input type="password" name="password"> </div>
密码字段包含提示。 没有一个,用户将无法理解需求,一旦他们尝试继续,这很可能会导致错误。
type="password"
属性通过用小黑点替换用户键入的内容来掩盖输入的值。 这是一种安全措施,如果他们碰巧在附近,它会阻止人们看到您输入的内容。
在用户键入时隐藏该值使得难以修复拼写错误。 因此,制作完成后,通常更容易删除整个条目并重新开始。 这是令人沮丧的,因为大多数用户使用的计算机并不是有人在看他们的肩膀。
由于拼写错误的风险增加,一些注册表单包括一个额外的“确认密码”字段。 这是一种预防措施,要求用户输入两次相同的密码,使工作量加倍并降低用户体验。 相反,最好让用户透露他们的密码,这符合原则 4 和 5,分别给予控制和提供选择。 这样一来,用户可以在知道没有人在看的时候选择透露他们的密码,从而降低拼写错误的风险。
最新版本的 Internet Explorer 和 Microsoft Edge 本机提供此行为。 由于我们将创建自己的解决方案,因此我们应该使用 CSS 来抑制此功能,如下所示:
input[type=password]::-ms-reveal { display: none; }

首先,我们需要在输入旁边注入一个按钮。 <button>
元素应该是你用 JavaScript 改变任何东西的首选元素——除了改变位置,这就是链接的用途。 单击时,它应该在password
和text
之间切换类型属性; 以及“显示”和“隐藏”之间的按钮标签。
function PasswordReveal(input) { // store input as a property of the instance // so that it can be referenced in methods // on the prototype this.input = input; this.createButton(); }; PasswordReveal.prototype.createButton = function() { // create a button this.button = $('<button type="button">Show password</button>'); // inject button $(this.input).parent().append(this.button); // listen to the button's click event this.button.on('click', $.proxy(this, 'onButtonClick')); }; PasswordReveal.prototype.onButtonClick = function(e) { // Toggle input type and button text if(this.input.type === 'password') { this.input.type = 'text'; this.button.text('Hide password'); } else { this.input.type = 'password'; this.button.text('Show password'); } };
#### JavaScript 语法和架构注释由于 JavaScript 有多种风格,以及构建组件的不同方式,我们将介绍用于构建密码显示组件的选择,以及本书中所有即将推出的组件。
首先,我们使用构造函数。 构造函数是一个通常用大写驼峰写的函数—— PasswordReveal
,而不是passwordReveal
。 它使用new
关键字初始化,这让我们可以使用相同的代码来创建组件的多个实例:
var passwordReveal1 = new PasswordReveal(document.getElementById('input1')); var passwordReveal2 = new PasswordReveal(document.getElementById('input2'));
其次,组件的方法是在原型上定义的——例如PasswordReveal.prototype.onButtonClick
。 原型是在同一组件的多个实例之间共享方法的最高效方式。

第三,jQuery 被用于创建和检索元素,以及监听事件。 虽然 jQuery 可能不是必需的或首选的,但使用它意味着本书可以专注于表单,而不是跨浏览器组件的复杂性。
如果你是一个会写一点代码的设计师,那么 jQuery 的普遍性和低门槛应该会有所帮助。 出于同样的原因,如果您不想使用 jQuery,那么重构组件以适应您的偏好不会有任何问题。
您可能还注意到了$.proxy
函数的使用。 这是 jQuery 对Function.prototype.bind
的实现。 如果我们不使用这个函数来监听事件,那么事件处理程序将在元素的上下文( this
)中被调用。 在上面的示例中, this.button
将是未定义的。 但是我们希望this
成为密码显示对象,以便我们可以访问它的属性和方法。
我们上面构建的密码显示界面在“显示密码”和“隐藏密码”之间切换按钮的标签。 更改按钮的标签时,一些屏幕阅读器用户可能会感到困惑; 一旦用户遇到一个按钮,他们希望该按钮能够持续存在。 即使按钮是持久的,更改标签也会使它看起来不是。
如果您的研究表明这是一个问题,您可以尝试两种替代方法。
首先,使用带有“显示密码”标签的复选框。 状态将由checked
的属性发出信号。 屏幕阅读器用户将听到“显示密码、复选框、选中”(或类似内容)。 有视力的用户将看到复选框勾号。 这种方法的问题是复选框用于输入数据,而不是控制界面。 一些用户可能认为他们的密码会泄露给系统。
或者,第二,改变按钮的state
——而不是标签。 要将状态传达给屏幕阅读器用户,您可以在true
(按下)和false
(未按下)之间切换aria-pressed
属性。
<button type="button" aria-pressed="true"> Show password </button>
聚焦按钮时,屏幕阅读器将宣布“显示密码,切换按钮,按下”(或类似内容)。 对于有视力的用户,您可以使用如下属性选择器将按钮样式设置为相应地显示为按下或未按下:
[aria-pressed="true"] { box-shadow: inset 0 0 0 0.15rem #000, inset 0.25em 0.25em 0 #fff; }
只需确保未按下和按下的样式明显且有区别,否则有视力的用户可能很难分辨它们之间的区别。
### 显微镜标签设置为“选择密码”而不是“密码”。 后者有点令人困惑,可能会提示用户输入他们已经拥有的密码,这可能是一个安全问题。 更微妙的是,它可能暗示用户已经注册,导致有认知障碍的用户认为他们正在登录。
在“密码”不明确的地方,“选择密码”提供了明确性。
## 按钮样式什么是按钮? 我们将网页上许多不同类型的组件称为按钮。 事实上,我已经介绍了两种不同类型的按钮,但没有说出来。 现在让我们这样做。
提交表单的按钮是“提交按钮”,它们通常编码为<input type="submit">
或<button type="submit">
。 <button>
元素更具延展性,因为您可以在其中嵌套其他元素。 但很少需要这样做。 大多数提交按钮只包含文本。
注意:在旧版本的 Internet Explorer 中,如果您有多个<button type="submit">
,表单会将所有按钮的值提交给服务器,而不管单击了哪个。 您需要知道单击了哪个按钮,以便确定要采取的正确行动方案,这就是应避免使用此元素的原因。
其他按钮被注入到界面中以增强使用 JavaScript 的体验——就像我们之前讨论的密码显示组件一样。 这也是一个<button>
但它的type
设置为button
(不是submit
)。
在这两种情况下,首先要了解按钮是它们不是链接。 链接通常带有下划线(通过用户代理样式)或特殊定位(在导航栏中),因此它们可以与常规文本区分开来。 将鼠标悬停在链接上时,光标将变为指针。 这是因为,与按钮不同,链接具有较弱的感知可供性。
在Resilient Web Design中,Jeremy Keith 讨论了材料诚实的概念。 他说:“一种材料不应该用作另一种材料的替代品。 否则最终结果是具有欺骗性的。” 让链接看起来像一个按钮在本质上是不诚实的。 它告诉用户链接和按钮是相同的,而实际上它们不是相同的。
链接可以做按钮不能做的事情。 例如,可以在新选项卡中打开链接或添加书签以供以后使用。 因此,按钮不应看起来像链接,也不应具有指针光标。 相反,我们应该让按钮看起来像按钮,它们自然具有很强的感知可供性。 它们是否具有圆角、阴影和边框取决于您,但无论如何它们都应该看起来像按钮。
例如,按钮仍然可以通过更改背景颜色来提供悬停(和焦点)反馈。
### 放置提交按钮通常位于表单的底部:对于大多数表单,用户从上到下填写字段,然后提交。 但是按钮应该左对齐、右对齐还是居中对齐? 要回答这个问题,我们需要考虑用户自然会在哪里寻找它。
字段标签和表单控件左对齐(在从左到右的阅读语言中)并从上到下运行。 用户将在最后一个字段下方查找下一个字段。 当然,提交按钮也应该位于该位置:在最后一个字段的左侧和正下方。 这也有助于放大用户,因为右对齐按钮更容易从屏幕上消失。
### 文本按钮的文本与其样式一样重要。 文本应明确描述正在采取的行动。 因为它是一个动作,所以它应该是一个动词。 我们的目标应该是使用尽可能少的单词,因为这样可以更快地阅读。 但是我们不应该以清晰为代价删除单词。
确切的词可以与您品牌的语气相匹配,但不要用清晰来换取古怪。
简单明了的语言,每个人都容易理解。 确切的词将取决于服务的类型。 对于我们的注册表,“注册”很好,但根据您的服务,“加入”或“注册”可能更合适。
## 验证尽管我们努力创造一种包容、简单且无摩擦的注册体验,但我们无法消除人为错误。 人们会犯错误,当他们犯错时,我们应该尽可能轻松地修复它们。
在表单验证方面,有许多重要的细节需要考虑。 从选择何时提供反馈,到如何显示反馈,再到制定好的错误信息——所有这些都需要考虑在内。
### HTML5 验证HTML5 验证已经存在了一段时间。 通过添加一些 HTML 属性,支持的浏览器将在提交表单时标记错误的字段。 不支持的浏览器会退回到服务器端验证。
通常我会推荐使用浏览器免费提供的功能,因为它通常更高效、更健壮且易于访问。 更不用说,随着越来越多的网站开始使用标准功能,用户会更加熟悉它。
虽然 HTML5 验证支持非常好,但它的实现并不统一。 例如, required 属性可以从一开始就将字段标记为无效,这是不可取的。 某些浏览器,例如 Firefox 45.7,即使用户在框中输入了一些内容,也会显示“请输入电子邮件地址”的错误,而例如 Chrome,会显示“请在电子邮件地址中包含一个 '@'”,这更有帮助。
我们还希望为用户提供相同的界面,无论是在服务器还是客户端捕获错误。 出于这些原因,我们将设计自己的解决方案。 首先要做的是关闭 HTML5 验证: <form novalidate>
当用户提交表单时,我们需要检查是否有错误。 如果有,我们需要阻止表单向服务器提交详细信息。
function FormValidator(form) { form.on('submit', $.proxy(this, 'onSubmit')); } FormValidator.prototype.onSubmit = function(e) { if(!this.validate()) { e.preventDefault(); // show errors } };
注意我们监听的是表单的提交事件,而不是按钮的点击事件。 当焦点位于其中一个字段内时,后者将阻止用户通过按Enter提交表单。 这也称为隐式表单提交。
### 显示反馈检测错误的存在非常好,但在这一点上用户并不明智。 界面的三个不同部分需要更新。 我们现在将讨论其中的每一个。
#### 文件名文档的<title>
是屏幕阅读器读出的网页的第一部分。 因此,我们可以使用它来快速通知用户他们的提交出现了问题。 当页面在服务器请求后重新加载时,这尤其有用。
尽管我们通过使用 JavaScript 在客户端捕获错误来增强用户体验,但并非所有错误都可以通过这种方式捕获。 例如,检查电子邮件地址是否尚未被使用只能在服务器上进行检查。 而且无论如何,JavaScript 很容易失败,所以我们不能仅仅依赖它的可用性。
如果原始页面标题可能显示为“Register for [service]”,则错误时应显示为“(2 个错误)Register for [service]”(或类似名称)。 确切的措辞在某种程度上取决于意见。
以下 JavaScript 更新了标题:
document.title = "(" + this.errors.length + ")" + document.title;
如上所述,这主要是针对屏幕阅读器用户的,但正如包容性设计的情况一样,对一组用户有帮助的也对其他所有人有帮助。 这次,更新的标题是选项卡中的通知。

错误摘要
与标题元素相比,错误摘要更加突出,它告诉有视力的用户出了问题。 但它也负责让用户了解出了什么问题以及如何修复它。
它位于页面顶部,因此用户无需在页面刷新后向下滚动即可查看它(如果服务器上发现错误)。 按照惯例,错误是红色的。 但是,仅依靠颜色可能会排除色盲用户。 要引起对摘要的注意,还可以考虑使用位置、大小、文本和图像。

该面板包括一个标题“有问题”,以指示问题。 请注意,它没有说“错误”这个词,这不是很友好。 想象一下,您正在填写您的详细信息以在展厅购买汽车并犯了一个错误。 销售人员不会说“错误”——事实上,如果他们真的这么说,那就太奇怪了。
<div class="errorSummary" role="group" tabindex="-1" aria-labelledby="errorSummary-heading"> <h2>There's a problem</h2> <ul> <li><a href="#emailaddress">Enter an email address</a></li> <li><a href="#password">The password must contain an uppercase letter</a></li> </ul> </div>
容器有一个role
group
,用于对一组界面元素进行分组:在这种情况下,标题和错误链接。 tabindex
属性设置为-1
,因此可以使用 JavaScript 以编程方式聚焦(当表单提交错误时)。 这可确保错误摘要面板滚动到视图中。 否则,提交时界面会出现无响应和损坏。
注意:使用tabindex="0"
意味着它将通过Tab键永久聚焦,这是 2.4.3 Focus Order WCAG 失败。 如果用户可以使用选项卡,他们希望它实际上会做一些事情。
FormValidator.prototype.showSummary = function () { // ... this.summary.focus(); };
在下面,有一个错误链接列表。 单击链接会将焦点设置到错误字段,从而使用户可以快速跳转到表单。 链接的href
属性设置为控件的 id,在某些浏览器中足以设置焦点。 但是,在其他浏览器中,单击链接只会将输入滚动到视图中,而不会将其聚焦。 为了解决这个问题,我们可以明确地关注输入。
FormValidator.prototype.onErrorClick = function(e) { e.preventDefault(); var href = e.target.href; var id = href.substring(href.indexOf("#"), href.length); $(id).focus(); };
当没有任何错误时,应该隐藏摘要面板。 这确保了页面上只有一个摘要面板,并且无论错误是由客户端还是服务器呈现,它始终显示在同一位置。 要隐藏面板,我们需要添加一个hidden
类。
<div class="errorSummary hidden" ...></div>
.hidden { display: none; }
注意:您可以使用hidden
属性/属性来切换元素的可见性,但对它的支持较少。 包容性设计是关于做出你知道不太可能排除人的决定。 使用类符合这一理念。
我们需要将相关的错误消息放在字段上方。 这节省了用户上下滚动页面以检查错误消息的时间,并让他们在表单中向下移动。 如果消息被放置在字段下方,我们会增加它被浏览器自动完成面板或屏幕键盘遮挡的机会。

<div class="field"> <label for="blah"> <span class="field-error"> <svg width="1.5em" height="1.5em"><use xmlns:xlink="https://www.w3.org/1999/xlink" xlink:href="#warning-icon"></use></svg> Enter your email address. </span> <span class="field-error">Enter an email address</span> </label> </div>
与前面提到的提示模式一样,错误消息被注入到标签内部。 当该字段被聚焦时,屏幕阅读器用户将在上下文中听到消息,因此他们可以在表单中自由移动,而无需参考摘要。
错误消息为红色,并使用 SVG 警告图标来引起用户注意。 如果我们只使用颜色变化来表示错误,这将排除色盲用户。 所以这对于有视力的用户来说非常有效——但是屏幕阅读器用户呢?
为了给有视力和无视力的用户提供同等的体验,我们可以使用得到良好支持的aria-invalid
属性。 当用户关注输入时,它现在会在屏幕阅读器中宣布“无效”(或类似的)。
<input aria-inval>
注意:注册表单仅包含文本输入。 在第 3 章“航班预订表单”中,我们将了解如何为单选按钮等字段组注入可访问的错误。
### 再次提交表单第二次提交表单时,我们需要从视图中清除现有的错误。 否则,用户可能会看到重复的错误。
FormValidator.prototype.onSubmit = function(e) { this.resetPageTitle(); this.resetSummaryPanel(); this.removeInlineErrors(); if(!this.validate()) { e.preventDefault(); this.updatePageTitle(); this.showSummaryPanel(); this.showInlineErrors(); } };
### 初始化完成了FormValidator
组件的定义,我们现在准备初始化它。 要创建FormValidator
的实例,您需要将表单元素作为第一个参数传递。
var validator = new FormValidator(document.getElementById('registration'));
例如,要验证电子邮件字段,请调用addValidator()
方法:
validator.addValidator('email', [{ method: function(field) { return field.value.trim().length > 0; }, message: 'Enter your email address.' },{ method: function(field) { return (field.value.indexOf('@') > -1); }, message: 'Enter the 'at' symbol in the email address.' }]);
第一个参数是控件的name
,第二个参数是规则对象数组。 每个规则包含两个属性: method
和message
。 该method
是一个测试各种条件以返回true
或false
的函数。 False 将该字段置于错误状态,如前所述,该状态用于使用错误填充界面。
在The Design of Everyday Things中,Don Norman 谈到了为错误而设计。 他谈到人们交谈的方式:
“如果一个人说了一些我们认为是假的话,我们就会质疑和辩论。 我们不发出警告信号。 我们不发出哔哔声。 我们不提供错误信息。 […] 在两个朋友之间的正常对话中,错误陈述被视为正常,作为对真正意思的近似。”
与人类不同,机器的智能不足以确定大多数行为的含义,但它们对错误的容忍度往往远远低于它们需要的容忍度。 Jared Spool 在“设计是否有节律性对立?”中对此开了个玩笑。 (大约 42 分钟):
“获取一个电话号码并去掉所有的破折号、括号和空格需要一行代码,而写一条你留下的错误消息需要十行代码。”
addValidator
方法(如上所示)演示了如何设计验证规则,以便它们可以原谅微不足道的错误。 例如,第一条规则在检查其长度之前修剪该值,从而减轻用户的负担。
实时内联验证在用户输入或离开字段时提供反馈( onblur
)。 有一些证据表明实时内联验证可以提高准确性并减少长表单的完成时间。 这部分与在用户脑海中对该领域的需求新鲜时向用户提供反馈有关。 但是实时内联验证(或简称为实时验证)带来了几个问题。
对于需要一定数量字符的条目,第一次击键将始终构成无效条目。 这意味着用户将被提前打断,这可能导致他们切换心理环境,从输入信息到修复信息。
或者,我们可以等到用户输入足够多的字符后再显示错误。 但这意味着用户只有在输入正确的值后才能得到反馈,这有点毫无意义。
我们可以等到用户离开该字段( onblur
),但这为时已晚,因为用户已经为下一个字段做好了心理准备(并且经常开始输入)。 此外,一些用户在使用表单时会切换窗口或使用密码管理器。 这样做会触发模糊事件,导致在用户完成之前显示错误。 都非常令人沮丧。
请记住,在不刷新页面的情况下向用户提供反馈是没有问题的。 将错误消息内联(在字段旁边)也没有问题——我们已经这样做了。 实时反馈的问题在于它会过早或过晚打断用户,这通常会导致不和谐的体验。
如果用户经常看到错误,那么其他地方可能有问题。 专注于缩短表单并提供更好的指导(良好的标签和提示文本)。 这样用户应该不会看到更多的奇怪错误。 我们将在下一章中研究更长的形式。
### 清单确认模式实时验证的一种变体涉及在用户键入时勾选规则(将它们标记为完整)。 这比实时验证侵入性更小,但并不适合每种类型的字段。 这是 MailChimp 的注册表单的示例,它在密码字段中采用了这种技术。

您应该将规则放在字段之上。 否则屏幕键盘可能会掩盖反馈。 因此,用户可能会停止输入并隐藏键盘以检查反馈。
### 关于禁用提交按钮的说明某些表单旨在禁用提交按钮,直到所有字段都变为有效。 这有几个问题。
首先,用户想知道他们的条目实际上有什么问题。 其次,禁用的按钮是不可聚焦的,这使得使用Tab键导航的盲人用户很难发现该按钮。 第三,禁用的按钮是灰色的,很难阅读。
当我们向用户提供明确的反馈时,当用户期望它时,没有充分的理由通过禁用按钮来夺走用户的控制权。
### 制作一个好的错误消息没有什么比内容更重要。 用户不会来到您的网站来享受设计。 他们来享受使用服务的内容或结果。
如果我们忽略用于制作错误消息的词语,即使是最深思熟虑、包容性和设计精美的体验也将一文不值。 一项研究表明,显示自定义错误消息可将转化率提高 0.5%,相当于年收入超过 250,000 英镑。
“内容就是用户体验。”
— 金妮·雷迪什
与标签、提示和任何其他内容一样,一个好的错误消息可以用尽可能少的文字提供清晰的信息。 通常,我们应该根据内容来驱动界面设计——而不是相反。 但在这种情况下,了解如何以及为什么显示错误信息会影响文字的设计。 这就是为什么 Jared Spool 说“内容和设计是不可分割的工作伙伴”。
我们在屏幕顶部和字段旁边的摘要中显示消息。 维护同一消息的两个版本很难获得令人信服的收益。 相反,设计一个在这两个地方都有效的错误消息。 “输入一个'at'符号”需要来自字段标签的上下文才能有意义。 “您的电子邮件地址需要一个 'at' 符号”在这两个地方都适用。
避免客套话,例如以“请”开头的每条错误消息。 一方面,这似乎是礼貌的; 另一方面,它妨碍并意味着选择。
无论您采取什么方法,由于内容的性质,都会有一些重复。 测试通常涉及提交表单而不输入任何信息。 这使得重复非常明显,这可能会导致我们大吃一惊。 但这种情况多久发生一次? 大多数用户并没有试图破坏界面。

不同的错误需要不同的格式。 “输入您的名字”之类的说明很自然。 但是“输入一个不超过 35 个字符的名字”比“名字必须不超过 35 个字符”这样的描述更长、更冗长、更不自然。
这是一个清单:
- 简明扼要。 不要使用不必要的单词,但不要以清晰为代价省略单词。
- 始终如一。 始终使用相同的语气、相同的词和相同的标点符号。
- 请明确点。 如果您知道为什么出了问题,请说出来。 “电子邮件无效。” 是模棱两可的,会给用户带来负担。 “电子邮件需要一个'at'符号”很清楚。
- 做人,避免行话。 不要使用无效、禁止和强制等词。
- 使用简单的语言。 错误消息不是宣传您品牌幽默语气的机会。
- 使用主动语态。 当错误是一条指令并且您告诉用户该做什么时。 例如,“输入您的姓名”,而不是“必须输入名字”。
- 不要责怪用户。 让他们知道出了什么问题以及如何解决。
在本章中,我们解决了几个基本的表单设计挑战,这些挑战远远超出了简单的注册表单。 在许多方面,本章既是关于不该做什么的,也是关于我们应该做什么的。 通过避免新颖和人为的节省空间的模式来专注于减少我们包含的字段数量,我们避免了几个可用性失败,同时使表单更令人愉快。
###要避免的事情- 使用
placeholder
属性作为存储标签和提示文本的机制。 - 使用不正确的输入类型。
- 样式按钮和链接相同。
- 在用户键入时验证字段。
- 禁用提交按钮。
- 使用复杂的行话和受品牌影响的显微文案。
就是这样。 如果您喜欢Form Design Patterns的第一章,您可以立即获得这本书。 快乐阅读!

电子书
19 美元获取电子书PDF、ePUB、Kindle。 粉碎会员免费。
精装
39 美元获取印刷品(包括电子书)印刷,质量精装。 全球免费航空邮寄。