Dark Dwarf Blog background

js-form-validation

JavaScript 表单验证

1. 客户端验证的两种方式

a.a. HTML5 内置验证

HTML5 为表单输入框提供了一套强大的内置验证机制,通过添加特定的属性即可启用:

  • required:必填字段。
  • type="email" / type="url":验证字段是否为有效的 Email 或 URL 格式。
  • minlength / maxlength:限制文本输入的最小/最大长度。
  • min / max:限制数字或日期输入的最小/最大值。
  • pattern:提供一个正则表达式,用于验证输入值是否匹配特定模式。
<form>
  <label for="email">Email:</label>
  <input type="email" id="email" name="email" required minlength="8">
  <button type="submit">Submit</button>
</form>

当用户尝试提交不符合上述规则的表单时,浏览器会自动显示默认的错误提示气泡。我们还可以使用 CSS 的 :valid:invalid 伪类来为有效和无效的输入框添加样式。

input:invalid {
  border: 2px solid red;
}
input:valid {
  border: 2px solid green;
}

b.b. JavaScript 验证

虽然 HTML5 内置验证很方便,但它的提示信息和外观由浏览器决定,难以自定义,并且无法实现一些复杂的逻辑(如“确认密码”是否与原密码一致)。

为了完全控制验证行为和错误消息的展示,我们需要使用 JavaScript,特别是强大的约束验证 API (Constraint Validation API)。下面我们展开讲解一下这个 API。

3. 约束验证 API

这是一套内建于浏览器、可用于表单元素上的属性和方法的集合。

a.a. 禁用浏览器默认验证

要使用 JavaScript 全权接管验证,首先需要阻止浏览器默认的验证行为。只需在 <form> 标签上添加 novalidate 属性即可。

<form novalidate>
  <!-- 表单内容 -->
</form>

这样,即使用户输入了无效数据,浏览器也不会再弹出默认的错误提示气泡,从而为我们的自定义提示留出空间。

b.b. ValidityState 对象:检查验证状态

每个表单字段元素 (如 <input>) 都有一个只读的 validity 属性。这个属性返回一个 ValidityState 对象,其中包含了一系列布尔值属性,详细描述了该字段当前的验证状态。

validity 对象就像一份输入框的“健康报告”。报告中有很多项,每一项都告诉我们该输入框是否存在某种特定的“病症”。

一些常用的 ValidityState 属性包括:

  • valid:如果所有验证都通过,则为 true。这是总的健康状况。
  • valueMissing:如果字段有 required 属性但值为空,则为 true
  • typeMismatch:如果 type 属性(如 email, url)的值格式不正确,则为 true
  • patternMismatch:如果值不匹配 pattern 属性定义的正则表达式,则为 true
  • tooShort / tooLong:如果值的长度小于 minlength 或大于 maxlength,则为 true
  • customError:如果通过 setCustomValidity() 设置了自定义错误,则为 true
const emailInput = document.getElementById('email');

// 当用户输入时检查
emailInput.addEventListener('input', () => {
  if (emailInput.validity.typeMismatch) {
    console.log("Please enter a valid email address!");
  }
});

c.c. validationMessage:获取错误消息

每个表单字段还有一个 validationMessage 属性。它会根据当前失败的验证约束,返回一条由浏览器生成的、本地化的、用户友好的错误消息。

// 如果 email 字段为空,emailInput.validationMessage 可能会是 "Please fill out this field."
// 如果格式不正确,可能会是 "Please include an '@' in the email address."
console.log(emailInput.validationMessage);

d.d. 核心方法:自定义验证逻辑

  1. checkValidity()

    • 调用一个表单元素(如 input)的 checkValidity() 方法,会检查该元素的 validity.valid 属性。如果为 false,它会触发一个 invalid 事件并返回 false;否则返回 true
    • <form> 元素上调用此方法,会检查其内部所有字段的有效性。
  2. setCustomValidity(message)

    • 这是最强大的方法,它允许我们设置自定义的错误消息。
    • 工作原理
      • 如果你调用 input.setCustomValidity("你的自定义错误消息"),那么该字段会立即变得无效 (validity.validfalse),并且其 validationMessage 会变成你设置的消息。
      • 如果你调用 input.setCustomValidity("") (传入一个空字符串),则会清除自定义错误,使该字段恢复到基于 HTML 属性的验证状态。

    经典案例:验证密码一致性

    const password = document.getElementById('password');
    const confirmPassword = document.getElementById('confirm-password');
    
    confirmPassword.addEventListener('input', () => {
      if (password.value !== confirmPassword.value) {
        // 设置自定义错误
        confirmPassword.setCustomValidity("Passwords do not match!");
      } else {
        // 清除自定义错误
        confirmPassword.setCustomValidity("");
      }
    });

4. 实践:构建自定义验证流程

下面是一个结合了实时验证和提交时验证的完整示例。

a.a. HTML 结构

为每个输入框配备一个用于显示错误消息的 <span> 元素。

<form novalidate>
  <div>
    <label for="email">Email:</label>
    <input type="email" id="email" name="email" required>
    <span class="error" aria-live="polite"></span>
  </div>
  <button type="submit">Submit</button>
</form>

b.b. JavaScript 实时验证

在用户输入时或离开字段时提供即时反馈。

const emailInput = document.getElementById('email');
const errorSpan = emailInput.nextElementSibling;

emailInput.addEventListener('input', (event) => {
  // 每次用户输入时,检查字段的有效性
  if (emailInput.validity.valid) {
    // 如果有效,清空错误消息
    errorSpan.textContent = '';
    errorSpan.className = 'error';
  } else {
    // 如果无效,显示错误
    showError();
  }
});

function showError() {
  if (emailInput.validity.valueMissing) {
    errorSpan.textContent = 'You need to enter an email address.';
  } else if (emailInput.validity.typeMismatch) {
    errorSpan.textContent = 'Entered value needs to be an email address.';
  }
  errorSpan.className = 'error active';
}

c.c. JavaScript 提交时验证

在用户点击提交按钮时,对整个表单进行最终检查。

const form = document.querySelector('form');

form.addEventListener('submit', (event) => {
  // 阻止表单默认的提交行为
  event.preventDefault();

  // 如果表单无效,显示错误
  if (!emailInput.validity.valid) {
    showError();
    return;
  }

  // 如果表单有效,可以执行提交逻辑
  alert("High five! Form submitted successfully.");
});