客户端表单验证
在将数据提交到服务器之前,务必确保所有必需的表单控件都已填写,并且格式正确。这称为**客户端表单验证**,有助于确保提交的数据与各种表单控件中设定的要求相匹配。本文将引导您了解客户端表单验证的基本概念和示例。
先决条件 | 计算机知识,对HTML、CSS和JavaScript有基本的了解。 |
---|---|
目标 | 了解什么是客户端表单验证、为什么它很重要以及如何应用各种技术来实现它。 |
客户端验证是一个初步检查,也是良好用户体验的重要功能;通过在客户端捕获无效数据,用户可以立即修复它。如果它到达服务器然后被拒绝,则由于往返服务器然后返回客户端以告知用户修复其数据而导致明显的延迟。
但是,**客户端验证不应被视为**一项详尽的安全措施!您的应用程序应始终对客户端和**服务器端**的任何表单提交数据执行安全检查,因为客户端验证很容易绕过,因此恶意用户仍然可以轻松地将错误数据发送到您的服务器。阅读网站安全以了解可能发生的情况;实现服务器端验证超出了本模块的范围,但您应该牢记这一点。
什么是表单验证?
访问任何带有注册表单的热门网站,您会注意到当您未按预期格式输入数据时,它们会提供反馈。您会收到以下消息
- "此字段是必需的"(您不能将此字段留空)。
- "请以 xxx-xxxx 的格式输入您的电话号码"(需要特定的数据格式才能被视为有效)。
- "请输入有效的电子邮件地址"(您输入的数据格式不正确)。
- "您的密码需要在 8 到 30 个字符之间,并且包含一个大写字母、一个符号和一个数字。"(您的数据需要非常特定的数据格式)。
这称为**表单验证**。当您输入数据时,浏览器和/或 Web 服务器将检查数据是否格式正确并在应用程序设置的约束范围内。在浏览器中完成的验证称为**客户端**验证,而在服务器上完成的验证称为**服务器端**验证。在本章中,我们将重点关注客户端验证。
如果信息格式正确,则应用程序允许将数据提交到服务器(通常)并保存在数据库中;如果信息格式不正确,则会向用户显示错误消息,说明需要更正的内容,并让他们重试。
我们希望使填写 Web 表单尽可能容易。那么,为什么我们坚持要验证我们的表单呢?主要有三个原因
- **我们希望获得正确格式的正确数据。**如果用户数据以错误的格式存储、不正确或完全省略,我们的应用程序将无法正常工作。
- **我们希望保护用户的数据。**强制用户输入安全密码可以更轻松地保护其帐户信息。
- **我们希望保护自己。**恶意用户可以使用许多方法滥用未受保护的表单来损害应用程序。请参阅网站安全。
**警告:**永远不要信任从客户端传递到服务器的数据。即使您的表单正在正确验证并防止在客户端输入错误格式的数据,恶意用户仍然可以更改网络请求。
客户端验证的不同类型
在 Web 上,您会遇到两种不同的客户端验证类型
- **内置表单验证**使用 HTML 表单验证功能,我们在本模块的许多地方都讨论过。此验证通常不需要太多 JavaScript。内置表单验证的性能优于 JavaScript,但它不如 JavaScript 验证灵活。
- **JavaScript** 验证使用 JavaScript 编写。此验证完全可自定义,但您需要全部创建它(或使用库)。
使用内置表单验证
现代表单控件最重要的功能之一是能够在不依赖 JavaScript 的情况下验证大多数用户数据。这是通过在表单元素上使用验证属性来完成的。我们在课程中已经见过很多这样的属性,但为了回顾一下
required
:指定在提交表单之前是否需要填写表单字段。minlength
和maxlength
:指定文本数据(字符串)的最小和最大长度。min
和max
:指定数值输入类型的最小值和最大值。type
:指定数据是否需要是数字、电子邮件地址或某些其他特定预设类型。pattern
:指定一个正则表达式,该表达式定义输入数据需要遵循的模式。
如果表单字段中输入的数据遵循上述属性指定的所有规则,则认为它是有效的。否则,则认为它是无效的。
当元素有效时,以下内容为真
- 该元素与
:valid
CSS 伪类匹配,允许您对有效元素应用特定样式。 - 如果用户尝试发送数据,浏览器将提交表单,前提是没有其他阻止它这样做的因素(例如,JavaScript)。
当元素无效时,以下内容为真
- 该元素与
:invalid
CSS 伪类匹配,有时还与其他 UI 伪类匹配(例如,:out-of-range
)具体取决于错误,这允许您对无效元素应用特定样式。 - 如果用户尝试发送数据,浏览器将阻止表单并显示错误消息。
**注意:**有几个错误会阻止表单提交,包括badInput
、patternMismatch
、rangeOverflow
或 rangeUnderflow
、stepMismatch
、tooLong
或 tooShort
、typeMismatch
、valueMissing
或 customError
。
内置表单验证示例
在本节中,我们将测试上面讨论的一些属性。
简单的起始文件
让我们从一个简单的示例开始:一个允许您选择更喜欢香蕉还是樱桃的输入。此示例涉及一个简单的文本<input>
,以及一个关联的<label>
和一个提交<button>
。在 GitHub 上找到源代码,网址为fruit-start.html,以及下面的实时示例。
<form>
<label for="choose">Would you prefer a banana or cherry?</label>
<input id="choose" name="i-like" />
<button>Submit</button>
</form>
input:invalid {
border: 2px dashed red;
}
input:valid {
border: 2px solid black;
}
首先,在硬盘驱动器上的新目录中复制 fruit-start.html
。
required 属性
最简单的 HTML 验证功能是required
属性。要使输入成为强制性输入,请将此属性添加到元素中。设置此属性后,元素将与:required
UI 伪类匹配,并且当输入为空时,表单将不会提交,并在提交时显示错误消息。在为空时,输入也将被视为无效,与:invalid
UI 伪类匹配。
向您的输入添加 required
属性,如下所示。
<form>
<label for="choose">Would you prefer a banana or cherry? (required)</label>
<input id="choose" name="i-like" required />
<button>Submit</button>
</form>
请注意示例文件中包含的 CSS
input:invalid {
border: 2px dashed red;
}
input:invalid:required {
background-image: linear-gradient(to right, pink, lightgreen);
}
input:valid {
border: 2px solid black;
}
此 CSS 使输入在无效时具有红色虚线边框,在有效时具有更细的黑色实线边框。当输入是必需的并且无效时,我们还添加了背景渐变。在下面的示例中尝试新的行为
**注意:**您可以在 GitHub 上找到此实时示例,网址为fruit-validation.html。另请参阅源代码。
尝试在没有值的情况下提交表单。请注意无效输入如何获得焦点,默认错误消息(“请填写此字段”)如何出现以及如何阻止表单发送。
在任何支持此属性的元素上存在 required
属性意味着该元素与:required
伪类匹配,无论它是否有值。如果<input>
没有值,则 input
将与:invalid
伪类匹配。
**注意:**为了获得良好的用户体验,请向用户指示何时需要表单字段。这不仅是良好的用户体验,而且是 WCAG 可访问性 指南的要求。此外,仅要求用户输入您实际需要的数据:例如,为什么您真的需要知道某人的性别或职称?
针对正则表达式进行验证
另一个有用的验证功能是pattern
属性,它期望其值为正则表达式。正则表达式 (regexp) 是一种模式,可用于匹配文本字符串中的字符组合,因此正则表达式非常适合表单验证,并在 JavaScript 中用于各种其他用途。
正则表达式非常复杂,我们不打算在本文章中详尽地介绍它们。以下是一些示例,让您对它们的工作原理有一个基本了解。
a
— 匹配一个字符a
(而不是b
、不是aa
,依此类推)。abc
— 匹配a
,后跟b
,后跟c
。ab?c
— 匹配a
,可选后跟单个b
,后跟c
。(ac
或abc
)ab*c
— 匹配a
,可选后跟任意数量的b
,后跟c
。(ac
、abc
、abbbbbc
,依此类推)。a|b
— 匹配一个字符a
或b
。abc|xyz
— 匹配精确的abc
或精确的xyz
(但不匹配abcxyz
或a
或y
等)。
这里还有很多其他可能性,我们在这里没有介绍。有关完整列表和更多示例,请参阅我们的 正则表达式 文档。
让我们实现一个示例。更新您的 HTML 以添加一个 pattern
属性,如下所示
<form>
<label for="choose">Would you prefer a banana or a cherry?</label>
<input id="choose" name="i-like" required pattern="[Bb]anana|[Cc]herry" />
<button>Submit</button>
</form>
这给了我们以下更新——请尝试一下
注意:您可以在 GitHub 上找到此示例的实时版本,网址为 fruit-pattern.html(另请参阅 源代码)。
在此示例中,<input>
元素接受四个可能值之一:字符串“banana”、“Banana”、“cherry”或“Cherry”。正则表达式区分大小写,但我们使用嵌套在方括号内的额外“Aa”模式使其支持大写和小写版本。
此时,尝试将 pattern
属性中的值更改为之前看到的一些示例,并查看这如何影响您可以输入的值以使输入值有效。尝试编写一些您自己的示例,并查看效果如何。尽可能使它们与水果相关,以便您的示例有意义!
如果 <input>
的非空值与正则表达式的模式不匹配,则 input
将匹配 :invalid
伪类。
注意:某些 <input>
元素类型不需要 pattern
属性来针对正则表达式进行验证。例如,指定 email
类型会根据格式良好的电子邮件地址模式或与电子邮件地址的逗号分隔列表匹配的模式验证输入值,如果它具有 multiple
属性。
注意:<textarea>
元素不支持 pattern
属性。
约束条目的长度
您可以使用 minlength
和 maxlength
属性来约束由 <input>
或 <textarea>
创建的所有文本字段的字符长度。如果字段具有值且该值的字符数少于 minlength
值或多于 maxlength
值,则该字段无效。
浏览器通常不允许用户在文本字段中键入超出预期的更长值。比仅仅使用 maxlength
更好的用户体验是在可访问的方式下提供字符计数反馈并让他们将其内容编辑到合适的大小。社交媒体上发帖时的字符限制就是一个例子。可以使用 JavaScript(包括 使用 maxlength
的解决方案)来提供此功能。
注意:如果以编程方式设置值,则永远不会报告长度约束。它们仅针对用户提供的输入报告。
约束条目的值
对于数字字段(即 <input type="number">
),可以使用 min
和 max
属性提供有效值的范围。如果字段包含此范围之外的值,则该字段将无效。
让我们看另一个例子。创建 fruit-start.html 文件的新副本。
现在删除 <body>
元素的内容,并将其替换为以下内容
<form>
<div>
<label for="choose">Would you prefer a banana or a cherry?</label>
<input
type="text"
id="choose"
name="i-like"
required
minlength="6"
maxlength="6" />
</div>
<div>
<label for="number">How many would you like?</label>
<input type="number" id="number" name="amount" value="1" min="1" max="10" />
</div>
<div>
<button>Submit</button>
</div>
</form>
- 在这里,您将看到我们已为
text
字段提供了 6 的minlength
和maxlength
,这与 banana 和 cherry 的长度相同。 - 我们还为
number
字段提供了 1 的min
和 10 的max
。在此范围之外输入的数字将显示为无效;用户将无法使用增量/减量箭头将值移动到此范围之外。如果用户手动输入此范围之外的数字,则数据无效。数字不是必需的,因此删除值仍将导致有效值。
以下是运行中的示例
注意:您可以在 GitHub 上找到此示例的实时版本,网址为 fruit-length.html。另请参阅 源代码。
注意:<input type="number">
(以及其他类型,例如 range
和 date
)还可以使用 step
属性,该属性指定使用输入控件(例如向上和向下数字按钮)时值将增加或减少的增量。在上面的示例中,我们没有包含 step
属性,因此该值默认为 1
。这意味着浮点数(如 3.2)也将显示为无效。
完整示例
这是一个完整的示例,用于演示 HTML 内置验证功能的使用。首先,一些 HTML
<form>
<fieldset>
<legend>
Do you have a driver's license?<span aria-label="required">*</span>
</legend>
<!-- While only one radio button in a same-named group can be selected at a time,
and therefore only one radio button in a same-named group having the "required"
attribute suffices in making a selection a requirement -->
<input type="radio" required name="driver" id="r1" value="yes" /><label
for="r1"
>Yes</label
>
<input type="radio" required name="driver" id="r2" value="no" /><label
for="r2"
>No</label
>
</fieldset>
<p>
<label for="n1">How old are you?</label>
<!-- The pattern attribute can act as a fallback for browsers which
don't implement the number input type but support the pattern attribute.
Please note that browsers that support the pattern attribute will make it
fail silently when used with a number field.
Its usage here acts only as a fallback -->
<input
type="number"
min="12"
max="120"
step="1"
id="n1"
name="age"
pattern="\d+" />
</p>
<p>
<label for="t1"
>What's your favorite fruit?<span aria-label="required">*</span></label
>
<input
type="text"
id="t1"
name="fruit"
list="l1"
required
pattern="[Bb]anana|[Cc]herry|[Aa]pple|[Ss]trawberry|[Ll]emon|[Oo]range" />
<datalist id="l1">
<option>Banana</option>
<option>Cherry</option>
<option>Apple</option>
<option>Strawberry</option>
<option>Lemon</option>
<option>Orange</option>
</datalist>
</p>
<p>
<label for="t2">What's your email address?</label>
<input type="email" id="t2" name="email" />
</p>
<p>
<label for="t3">Leave a short message</label>
<textarea id="t3" name="msg" maxlength="140" rows="5"></textarea>
</p>
<p>
<button>Submit</button>
</p>
</form>
现在是一些用于设置 HTML 样式的 CSS
form {
font: 1em sans-serif;
max-width: 320px;
}
p > label {
display: block;
}
input[type="text"],
input[type="email"],
input[type="number"],
textarea,
fieldset {
width: 100%;
border: 1px solid #333;
box-sizing: border-box;
}
input:invalid {
box-shadow: 0 0 5px 1px red;
}
input:focus:invalid {
box-shadow: none;
}
呈现效果如下
有关可用于约束输入值和支持它们的输入类型的属性的完整列表,请参阅 与验证相关的属性。
注意:您可以在 GitHub 上找到此示例的实时版本,网址为 full-example.html(另请参阅 源代码)。
使用 JavaScript 验证表单
如果您想控制原生错误消息的外观和感觉,则必须使用 JavaScript。在本节中,我们将了解执行此操作的不同方法。
约束验证 API
约束验证 API 由以下表单元素 DOM 接口上提供的一组方法和属性组成
HTMLButtonElement
(表示<button>
元素)HTMLFieldSetElement
(表示<fieldset>
元素)HTMLInputElement
(表示<input>
元素)HTMLOutputElement
(表示<output>
元素)HTMLSelectElement
(表示<select>
元素)HTMLTextAreaElement
(表示<textarea>
元素)
约束验证 API 在上述元素上提供了以下属性。
validationMessage
:返回一个本地化消息,描述控件不满足的验证约束(如果有)。如果控件不是约束验证的候选对象(willValidate
为false
)或元素的值满足其约束(有效),则将返回空字符串。validity
:返回一个ValidityState
对象,其中包含几个描述元素有效性状态的属性。您可以在ValidityState
参考页面中找到所有可用属性的完整详细信息;下面列出了一些更常见的属性patternMismatch
:如果值与指定的pattern
不匹配,则返回true
,如果匹配,则返回false
。如果为 true,则元素匹配:invalid
CSS 伪类。tooLong
:如果值长于maxlength
属性指定的最大长度,则返回true
,如果短于或等于最大长度,则返回false
。如果为 true,则元素匹配:invalid
CSS 伪类。tooShort
:如果值短于minlength
属性指定的最小长度,则返回true
,如果长于或等于最小长度,则返回false
。如果为 true,则元素匹配:invalid
CSS 伪类。rangeOverflow
:如果值大于max
属性指定的最大值,则返回true
,如果小于或等于最大值,则返回false
。如果为 true,则元素匹配:invalid
和:out-of-range
CSS 伪类。rangeUnderflow
:如果值小于min
属性指定的最小值,则返回true
,如果大于或等于最小值,则返回false
。如果为 true,则元素匹配:invalid
和:out-of-range
CSS 伪类。typeMismatch
:如果值不符合所需的语法(当type
为email
或url
时),则返回true
,如果语法正确,则返回false
。如果为true
,则元素匹配:invalid
CSS 伪类。valid
:如果元素满足所有验证约束,因此被认为有效,则返回true
,如果失败任何约束,则返回false
。如果为 true,则元素匹配:valid
CSS 伪类;否则匹配:invalid
CSS 伪类。valueMissing
:如果元素具有required
属性但没有值,则返回true
,否则返回false
。如果为 true,则元素匹配:invalid
CSS 伪类。
willValidate
:如果提交表单时将验证元素,则返回true
;否则返回false
。
约束验证 API 还使上述元素和 form
元素可以使用以下方法。
checkValidity()
:如果元素的值没有有效性问题,则返回true
;否则返回false
。如果元素无效,则此方法还会在元素上触发invalid
事件。reportValidity()
:使用事件报告无效字段。此方法与onSubmit
事件处理程序中的preventDefault()
结合使用非常有用。setCustomValidity(message)
:向元素添加自定义错误消息;如果设置了自定义错误消息,则元素将被视为无效,并将显示指定的错误。这使您可以使用 JavaScript 代码来建立除标准 HTML 验证约束提供的约束之外的其他验证失败。在报告问题时,该消息将显示给用户。
实现自定义错误消息
正如您在前面 HTML 验证约束示例中看到的,每次用户尝试提交无效表单时,浏览器都会显示一条错误消息。此消息的显示方式取决于浏览器。
这些自动消息有两个缺点
- 无法通过 CSS 以标准方式更改其外观。
- 它们依赖于浏览器语言环境,这意味着您可能在一个语言的页面中看到另一个语言的错误消息,如下面的 Firefox 屏幕截图所示。
自定义这些错误消息是约束验证 API 最常见的用例之一。让我们一起完成一个简单的示例,了解如何做到这一点。
我们将从一些简单的 HTML 开始(您可以将其放入空白的 HTML 文件中;如果您愿意,可以使用 fruit-start.html 的新副本作为基础)
<form>
<label for="mail">
I would like you to provide me with an email address:
</label>
<input type="email" id="mail" name="mail" />
<button>Submit</button>
</form>
并在页面中添加以下 JavaScript 代码
const email = document.getElementById("mail");
email.addEventListener("input", (event) => {
if (email.validity.typeMismatch) {
email.setCustomValidity("I am expecting an email address!");
} else {
email.setCustomValidity("");
}
});
在这里,我们存储了对电子邮件输入的引用,然后向其添加了一个事件监听器,该监听器在输入内容中的值每次更改时都会运行包含的代码。
在包含的代码中,我们检查电子邮件输入的 validity.typeMismatch
属性是否返回 true
,这意味着包含的值与格式良好的电子邮件地址的模式不匹配。如果是,我们使用自定义消息调用 setCustomValidity()
方法。这会使输入无效,因此当您尝试提交表单时,提交会失败并显示自定义错误消息。
如果 validity.typeMismatch
属性返回 false
,我们使用空字符串调用 setCustomValidity()
方法。这会使输入有效,因此表单将提交。
您可以在下面尝试一下
注意:您可以在 GitHub 上找到此示例的在线版本,网址为 custom-error-message.html(另请参阅 源代码)。
更详细的示例
现在我们已经看到了一个非常简单的示例,让我们看看如何使用此 API 来构建一些稍微复杂一点的自定义验证。
首先,是 HTML。同样,您可以随时与我们一起构建它
<form novalidate>
<p>
<label for="mail">
<span>Please enter an email address:</span>
<input type="email" id="mail" name="mail" required minlength="8" />
<span class="error" aria-live="polite"></span>
</label>
</p>
<button>Submit</button>
</form>
此简单表单使用 novalidate
属性关闭浏览器的自动验证;这允许我们的脚本控制验证。但是,这不会禁用对约束验证 API 或应用 CSS 伪类(如 :valid
等)的支持。这意味着即使浏览器不会在发送数据之前自动检查表单的有效性,您仍然可以自己执行此操作并相应地设置表单样式。
我们要验证的输入是 <input type="email">
,它是 required
,并且具有 8 个字符的 minlength
。让我们使用自己的代码检查这些内容,并为每个内容显示自定义错误消息。
我们的目标是在 <span>
元素内显示错误消息。<span>
上设置了 aria-live
属性,以确保我们的自定义错误消息会呈现给所有人,包括将其读给屏幕阅读器用户。
注意:这里的一个关键点是,在表单上设置 novalidate
属性会阻止表单显示其自己的错误消息气泡,并允许我们改为以我们自己选择的方式在 DOM 中显示自定义错误消息。
现在,让我们使用一些基本的 CSS 来稍微改善表单的外观,并在输入数据无效时提供一些视觉反馈
body {
font: 1em sans-serif;
width: 200px;
padding: 0;
margin: 0 auto;
}
p * {
display: block;
}
input[type="email"] {
appearance: none;
width: 100%;
border: 1px solid #333;
margin: 0;
font-family: inherit;
font-size: 90%;
box-sizing: border-box;
}
/* This is our style for the invalid fields */
input:invalid {
border-color: #900;
background-color: #fdd;
}
input:focus:invalid {
outline: none;
}
/* This is the style of our error messages */
.error {
width: 100%;
padding: 0;
font-size: 80%;
color: white;
background-color: #900;
border-radius: 0 0 5px 5px;
box-sizing: border-box;
}
.error.active {
padding: 0.3em;
}
现在让我们看看实现自定义错误验证的 JavaScript 代码。
// There are many ways to pick a DOM node; here we get the form itself and the email
// input box, as well as the span element into which we will place the error message.
const form = document.querySelector("form");
const email = document.getElementById("mail");
const emailError = document.querySelector("#mail + span.error");
email.addEventListener("input", (event) => {
// Each time the user types something, we check if the
// form fields are valid.
if (email.validity.valid) {
// In case there is an error message visible, if the field
// is valid, we remove the error message.
emailError.textContent = ""; // Reset the content of the message
emailError.className = "error"; // Reset the visual state of the message
} else {
// If there is still an error, show the correct error
showError();
}
});
form.addEventListener("submit", (event) => {
// if the email field is valid, we let the form submit
if (!email.validity.valid) {
// If it isn't, we display an appropriate error message
showError();
// Then we prevent the form from being sent by canceling the event
event.preventDefault();
}
});
function showError() {
if (email.validity.valueMissing) {
// If the field is empty,
// display the following error message.
emailError.textContent = "You need to enter an email address.";
} else if (email.validity.typeMismatch) {
// If the field doesn't contain an email address,
// display the following error message.
emailError.textContent = "Entered value needs to be an email address.";
} else if (email.validity.tooShort) {
// If the data is too short,
// display the following error message.
emailError.textContent = `Email should be at least ${email.minLength} characters; you entered ${email.value.length}.`;
}
// Set the styling appropriately
emailError.className = "error active";
}
注释解释得很好,但简单来说
- 每次我们更改输入值时,我们都会检查它是否包含有效数据。如果包含,则会删除显示的任何错误消息。如果数据无效,则运行
showError()
以显示相应的错误。 - 每次我们尝试提交表单时,我们都会再次检查数据是否有效。如果是,则允许表单提交。如果不是,则运行
showError()
以显示相应的错误,并使用preventDefault()
阻止表单提交。 showError()
函数使用输入的validity
对象的各种属性来确定错误是什么,然后显示相应的错误消息。
以下是实际结果
注意:您可以在 GitHub 上找到此示例的在线版本,网址为 detailed-custom-validation.html。另请参阅 源代码。
约束验证 API 为您提供了一个强大的工具来处理表单验证,让您可以对用户界面进行极大的控制,超出您仅使用 HTML 和 CSS 可以实现的范围。
在没有内置 API 的情况下验证表单
在某些情况下,例如 自定义控件,您将无法或不想使用约束验证 API。您仍然可以使用 JavaScript 来验证表单,但您需要自己编写代码。
要验证表单,请考虑以下几个问题
- 我应该执行哪种类型的验证?
-
您需要确定如何验证数据:字符串操作、类型转换、正则表达式等。这取决于您。
- 如果表单未通过验证,我应该怎么做?
-
这显然是一个 UI 问题。您必须决定表单的行为方式。表单是否无论如何都会发送数据?是否应该突出显示错误的字段?是否应该显示错误消息?
- 如何帮助用户更正无效数据?
-
为了减少用户的挫败感,提供尽可能多的有帮助的信息来指导他们更正输入非常重要。您应该提供预先的建议,让他们知道预期是什么,以及清晰的错误消息。如果您想深入研究表单验证 UI 要求,以下是一些您应该阅读的有用文章
一个不使用约束验证 API 的示例
为了说明这一点,以下是前面示例的简化版本,不使用约束验证 API。
HTML 几乎相同;我们只是删除了 HTML 验证功能。
<form>
<p>
<label for="mail">
<span>Please enter an email address:</span>
<input type="text" id="mail" name="mail" />
<span class="error" aria-live="polite"></span>
</label>
</p>
<button>Submit</button>
</form>
同样,CSS 不需要做太多更改;我们只是将 :invalid
CSS 伪类转换为真正的类,并避免使用属性选择器。
body {
font: 1em sans-serif;
width: 200px;
padding: 0;
margin: 0 auto;
}
form {
max-width: 200px;
}
p * {
display: block;
}
input#mail {
appearance: none;
width: 100%;
border: 1px solid #333;
margin: 0;
font-family: inherit;
font-size: 90%;
box-sizing: border-box;
}
/* This is our style for the invalid fields */
input#mail.invalid {
border-color: #900;
background-color: #fdd;
}
input:focus.invalid {
outline: none;
}
/* This is the style of our error messages */
.error {
width: 100%;
padding: 0;
font-size: 80%;
color: white;
background-color: #900;
border-radius: 0 0 5px 5px;
box-sizing: border-box;
}
.error.active {
padding: 0.3em;
}
最大的变化在于 JavaScript 代码,它需要做更多繁重的工作。
const form = document.querySelector("form");
const email = document.getElementById("mail");
const error = email.nextElementSibling;
// As per the HTML Specification
const emailRegExp =
/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
// Now we can rebuild our validation constraint
// Because we do not rely on CSS pseudo-class, we have to
// explicitly set the valid/invalid class on our email field
window.addEventListener("load", () => {
// Here, we test if the field is empty (remember, the field is not required)
// If it is not, we check if its content is a well-formed email address.
const isValid = email.value.length === 0 || emailRegExp.test(email.value);
email.className = isValid ? "valid" : "invalid";
});
// This defines what happens when the user types in the field
email.addEventListener("input", () => {
const isValid = email.value.length === 0 || emailRegExp.test(email.value);
if (isValid) {
email.className = "valid";
error.textContent = "";
error.className = "error";
} else {
email.className = "invalid";
}
});
// This defines what happens when the user tries to submit the data
form.addEventListener("submit", (event) => {
event.preventDefault();
const isValid = email.value.length === 0 || emailRegExp.test(email.value);
if (!isValid) {
email.className = "invalid";
error.textContent = "I expect an email, darling!";
error.className = "error active";
} else {
email.className = "valid";
error.textContent = "";
error.className = "error";
}
});
结果如下所示
如您所见,自己构建验证系统并不难。困难的部分是使其通用到足以跨平台使用,并在您可能创建的任何表单上使用。有许多库可用于执行表单验证,例如 Validate.js。
测试你的技能!
您已经读完了本文,但您能记住最重要的信息吗?在继续之前,您可以进行一些进一步的测试以验证您是否保留了这些信息——请参阅 测试您的技能:表单验证。
总结
如果您想要自定义样式和错误消息,客户端表单验证有时需要 JavaScript,但它始终要求您仔细考虑用户。始终记住帮助您的用户更正他们提供的数据。为此,请务必
- 显示明确的错误消息。
- 对输入格式宽容。
- 准确指出错误发生的位置,尤其是在大型表单中。
验证表单填写正确后,即可提交表单。接下来,我们将介绍 发送表单数据。