Back

使用 CSS 为表单的有效和无效状态添加样式

使用 CSS 为表单的有效和无效状态添加样式

在用户输入任何字符之前,必填字段就显示红色警告。这是许多表单带来的糟糕体验——而使用现代 CSS 完全可以避免这种情况。

本指南介绍 CSS 表单验证状态以及控制它们的伪类。您将学习何时使用 :valid:invalid,为什么 :user-valid:user-invalid 能提供更好的用户体验,以及如何使用 CSS 为表单错误添加样式,包括使用 :has() 等模式来为父元素添加样式。

核心要点

  • HTML5 约束验证通过 requiredtypeminmaxpattern 等属性自动工作
  • 避免使用 :valid:invalid 来添加错误样式,因为它们在用户交互之前就会触发
  • 使用 :user-valid:user-invalid 仅在用户与字段交互后才显示验证反馈
  • :has() 与验证伪类结合使用,为父元素添加样式并控制错误消息的可见性
  • 始终将视觉验证与 ARIA 属性配对,以实现屏幕阅读器的可访问性

约束验证的工作原理

HTML5 约束验证会自动执行。当您向表单字段添加 requiredtype="email"minmaxminlengthmaxlengthpattern 等属性时,浏览器会持续评估有效性。

浏览器会在输入值发生变化时检查约束。CSS 伪类实时反映这种有效性状态,让您可以根据字段是否通过验证来为其添加样式。

<input type="email" required minlength="5" />

如果该字段为空、值不符合电子邮件格式或长度少于五个字符,则该字段将变为无效。

核心 CSS 表单伪类

:valid 和 :invalid

这些伪类根据当前有效性匹配字段:

input:valid {
  border-color: green;
}

input:invalid {
  border-color: red;
}

问题在于::invalid 在页面加载时立即应用。没有值的必填字段在用户触碰之前在技术上是无效的。这会造成糟糕的体验——用户在还没做错任何事情之前就看到了错误。

:required 和 :optional

这些伪类根据 required 属性来定位字段:

input:required {
  border-left: 3px solid blue;
}

input:optional {
  border-left: 3px solid gray;
}

用于显示哪些字段必须完成的视觉指示器。

:in-range 和 :out-of-range

用于带有 min/max 约束的数字和范围输入:

input:in-range {
  background: white;
}

input:out-of-range {
  background: #ffe0e0;
}

使用 :user-valid 和 :user-invalid 提供更好的用户体验

:user-valid:user-invalid 伪类解决了过早显示错误的问题。它们仅在用户与字段交互后才匹配——通常是在编辑和失去焦点之后。

input:user-invalid {
  border-color: red;
}

input:user-valid {
  border-color: green;
}

现在必填字段不会在页面加载时显示错误。验证样式仅在用户有机会输入数据后才出现。这符合用户期望并减少挫败感。

这些伪类具有广泛的浏览器支持,应该是您使用 CSS 为表单错误添加样式的默认选择。

使用 :has() 为父元素添加样式

:has() 选择器使得根据子元素的验证状态为父元素添加样式成为可能——这在以前没有 JavaScript 是无法实现的。

<div class="field">
  <label for="email">Email</label>
  <input type="email" id="email" required />
  <span class="error">请输入有效的电子邮件地址</span>
</div>
.error {
  display: none;
  color: red;
}

.field:has(input:user-invalid) .error {
  display: block;
}

.field:has(input:user-invalid) input {
  border-color: red;
}

这种模式仅在包含的输入在用户交互后验证失败时才显示错误消息。父容器控制同级错误元素的可见性。

可访问性考虑

仅有视觉样式不足以实现可访问的表单。屏幕阅读器用户需要程序化的错误关联。

将 CSS 验证状态与适当的 ARIA 属性配对:

<div class="field">
  <label for="email">Email</label>
  <input 
    type="email" 
    id="email" 
    required
    aria-describedby="email-error"
    aria-invalid="false"
  />
  <span id="email-error" class="error" aria-live="polite">
    请输入有效的电子邮件地址
  </span>
</div>

关键的可访问性要求:

  • aria-describedby 将输入与其错误消息关联
  • aria-live="polite" 向屏幕阅读器通知错误变化
  • aria-invalid 应在验证失败时通过 JavaScript 更新
  • 颜色对比度必须符合 WCAG 要求(文本为 4.5:1)
  • 不要仅依赖颜色——在颜色变化的同时使用图标或文本

实用模式:完整的字段验证

以下是结合这些技术的生产就绪模式:

/* 基础字段样式 */
.field input {
  border: 2px solid #ccc;
  transition: border-color 0.2s;
}

/* 交互后的有效状态 */
.field:has(input:user-valid) input {
  border-color: #2e7d32;
}

/* 交互后的无效状态 */
.field:has(input:user-invalid) input {
  border-color: #c62828;
}

.field:has(input:user-invalid) .error {
  display: block;
}

/* 默认隐藏错误消息 */
.error {
  display: none;
  color: #c62828;
  font-size: 0.875rem;
  margin-top: 0.25rem;
}

这种方法将错误消息保持隐藏状态,直到用户与字段交互,然后仅在验证失败时才显示它们。

结论

现代 CSS 表单伪类无需 JavaScript 即可提供强大的验证样式。使用 :user-valid:user-invalid 作为您的主要工具——它们可以防止过早显示错误并符合用户期望。将它们与 :has() 结合使用,实现控制错误消息可见性的父级样式。

请记住,视觉样式是对可访问错误处理的补充,而不是替代。始终将 CSS 验证状态与适当的 ARIA 属性配对,以服务屏幕阅读器用户。

常见问题

:invalid 伪类匹配任何验证失败的字段,包括在用户交互之前的页面加载时。:user-invalid 伪类仅在用户与字段交互并离开后才匹配。这可以防止在用户有机会填写之前在空的必填字段上显示错误样式。

这些伪类在现代浏览器中具有广泛的支持,包括 Chrome、Firefox、Safari 和 Edge。对于较旧的浏览器支持,可以考虑使用 :focus 和 :not(:placeholder-shown) 伪类作为后备模式,尽管这种方法不太精确。

CSS 验证伪类反映浏览器的约束验证 API 状态。如果您阻止默认表单提交或使用 JavaScript 处理表单,请确保在表单元素上调用 checkValidity()。CSS 状态根据 validity 属性更新,而不是提交尝试。

在父容器上使用 :has() 选择器来检测子输入验证状态。例如,.field:has(input:user-invalid) .error-message 可以让您根据该容器内的输入在用户交互后是否验证失败来显示或隐藏同级错误元素。

Truly understand users experience

See every user interaction, feel every frustration and track all hesitations with OpenReplay — the open-source digital experience platform. It can be self-hosted in minutes, giving you complete control over your customer data. . Check our GitHub repo and join the thousands of developers in our community..

OpenReplay