好的,我们开始今天的讲座。今天的主题是HTML表单中的novalidate属性,以及它如何禁用原生验证,同时保留约束验证API的可用性。这在需要自定义表单验证逻辑时非常有用。
什么是HTML表单验证?
在讨论novalidate之前,我们需要理解HTML表单验证的基本概念。HTML5引入了一套内置的表单验证机制,允许浏览器自动检查用户输入的数据是否符合预期的格式和约束。这些约束可以通过HTML属性来指定,例如:
required: 指示字段必须填写。type: 指定字段的类型,例如email、number、url等,浏览器会根据类型进行验证。minlength和maxlength: 指定文本字段的最小和最大长度。min和max: 指定数字字段的最小值和最大值。pattern: 使用正则表达式定义字段的有效格式。
当用户尝试提交表单时,浏览器会自动检查这些约束。如果发现任何错误,会阻止表单提交,并显示相应的错误消息。
novalidate属性的作用
novalidate属性用于禁用浏览器提供的原生表单验证。它可以应用于<form>元素或<input>/<button>元素。
- 应用于
<form>元素: 禁用整个表单的验证。 - 应用于
<input>/<button>元素: 禁用特定元素的验证。 
为什么禁用原生验证?
虽然原生验证很方便,但在某些情况下,我们可能需要禁用它,原因如下:
- 自定义验证逻辑: 原生验证提供的功能有限,可能无法满足所有需求。例如,我们可能需要根据多个字段的值进行验证,或者需要进行服务器端验证。
 - 自定义错误消息: 原生验证的错误消息通常是浏览器默认的,可能不够友好或不符合网站的风格。
 - 更精细的控制: 我们可能希望在用户输入时进行实时验证,而不是等到提交时才进行验证。或者,我们可能希望以不同的方式处理验证错误,例如,通过显示自定义的错误提示或通过改变字段的样式。
 - 复杂的交互: 有些表单可能包含复杂的交互,例如,根据用户的选择动态显示或隐藏某些字段。在这种情况下,原生验证可能会干扰这些交互。
 
约束验证API (Constraint Validation API)
即使禁用了原生验证,我们仍然可以使用约束验证API来手动执行验证。约束验证API提供了一组方法和属性,用于检查元素的有效性,并获取有关验证错误的信息。
以下是一些常用的约束验证API成员:
| 方法/属性 | 描述 | 
|---|---|
validity | 
返回一个ValidityState对象,该对象包含有关元素有效性的信息。 | 
validationMessage | 
返回一个字符串,包含有关元素验证错误的本地化消息。 | 
checkValidity() | 
如果元素有效,则返回true;否则,返回false。如果元素无效,还会触发invalid事件。 | 
reportValidity() | 
如果元素有效,则返回true;否则,返回false。如果元素无效,还会触发invalid事件,并显示浏览器的默认错误消息 (除非禁用了原生验证)。这个方法与checkValidity()的不同之处在于,它会触发浏览器显示错误消息,即使禁用了原生验证。 | 
setCustomValidity(message) | 
设置元素的自定义有效性消息。如果message为空字符串,则表示元素有效;否则,表示元素无效,并且validationMessage属性将返回该消息。 | 
使用novalidate和约束验证API的示例
下面是一个示例,演示如何使用novalidate属性禁用原生验证,并使用约束验证API手动执行验证:
<!DOCTYPE html>
<html>
<head>
  <title>novalidate Example</title>
  <style>
    .error {
      color: red;
    }
  </style>
</head>
<body>
  <form id="myForm" novalidate>
    <label for="email">Email:</label>
    <input type="email" id="email" name="email" required>
    <span id="emailError" class="error"></span>
    <br><br>
    <label for="age">Age:</label>
    <input type="number" id="age" name="age" min="18">
    <span id="ageError" class="error"></span>
    <br><br>
    <button type="submit">Submit</button>
  </form>
  <script>
    const form = document.getElementById('myForm');
    const emailInput = document.getElementById('email');
    const ageInput = document.getElementById('age');
    const emailError = document.getElementById('emailError');
    const ageError = document.getElementById('ageError');
    form.addEventListener('submit', function(event) {
      event.preventDefault(); // Prevent the default form submission
      let isValid = true;
      // Email validation
      if (!emailInput.checkValidity()) {
        emailError.textContent = emailInput.validationMessage; // Use browser's validation message
        isValid = false;
      } else {
        emailError.textContent = '';
      }
      // Age validation
      if (!ageInput.checkValidity()) {
        ageError.textContent = ageInput.validationMessage; // Use browser's validation message
        isValid = false;
      } else {
        ageError.textContent = '';
      }
      if (isValid) {
        // Form is valid, submit the data (e.g., using AJAX)
        alert('Form submitted successfully!');
        // Here you would typically send the form data to the server
        // using AJAX or another method.
      } else {
        alert('Form has errors. Please correct them.');
      }
    });
  </script>
</body>
</html>
在这个示例中:
- 我们使用
novalidate属性禁用了表单的原生验证。 - 我们添加了用于显示错误消息的
<span>元素。 - 在JavaScript中,我们监听表单的
submit事件,并阻止默认的表单提交行为。 - 我们使用
checkValidity()方法检查每个字段的有效性。 - 如果字段无效,我们使用
validationMessage属性获取浏览器提供的默认错误消息,并将其显示在相应的<span>元素中。 - 如果所有字段都有效,我们显示一个成功的提示框。
 
自定义验证消息
我们可以使用setCustomValidity()方法设置自定义的验证消息。这允许我们提供更友好和更具描述性的错误消息。
<!DOCTYPE html>
<html>
<head>
  <title>Custom Validation Message Example</title>
  <style>
    .error {
      color: red;
    }
  </style>
</head>
<body>
  <form id="myForm" novalidate>
    <label for="username">Username:</label>
    <input type="text" id="username" name="username" required pattern="^[a-zA-Z0-9]+$">
    <span id="usernameError" class="error"></span>
    <br><br>
    <button type="submit">Submit</button>
  </form>
  <script>
    const form = document.getElementById('myForm');
    const usernameInput = document.getElementById('username');
    const usernameError = document.getElementById('usernameError');
    usernameInput.addEventListener('input', function() {
      if (usernameInput.validity.patternMismatch) {
        usernameInput.setCustomValidity("Username must contain only letters and numbers.");
      } else {
        usernameInput.setCustomValidity(""); // Clear the custom error message
      }
      usernameInput.reportValidity(); // Force the display of the error message
    });
    form.addEventListener('submit', function(event) {
      event.preventDefault(); // Prevent the default form submission
      if (!form.checkValidity()) {
        usernameError.textContent = usernameInput.validationMessage;
        alert('Form has errors. Please correct them.');
      } else {
        usernameError.textContent = '';
        alert('Form submitted successfully!');
      }
    });
  </script>
</body>
</html>
在这个示例中:
- 我们使用
pattern属性定义了用户名必须包含字母和数字的正则表达式。 - 我们添加了一个
input事件监听器,以便在用户输入时实时更新自定义验证消息。 - 如果用户名不符合模式,我们使用
setCustomValidity()方法设置自定义的错误消息。 - 如果用户名符合模式,我们使用
setCustomValidity("")清除自定义的错误消息。这很重要,因为如果自定义错误消息不为空,浏览器会认为该字段无效,即使它实际上是有效的。 - 在
submit事件处理程序中,我们检查表单的有效性,并显示自定义的错误消息(如果存在)。注意我们这里使用了reportValidity(),确保错误信息显示,即使form上禁用了原生验证。 
实时验证
我们可以在用户输入时进行实时验证,而不是等到提交时才进行验证。这可以通过监听input、change或blur事件来实现。
<!DOCTYPE html>
<html>
<head>
  <title>Real-time Validation Example</title>
  <style>
    .error {
      color: red;
    }
  </style>
</head>
<body>
  <form id="myForm" novalidate>
    <label for="password">Password:</label>
    <input type="password" id="password" name="password" required minlength="8">
    <span id="passwordError" class="error"></span>
    <br><br>
    <label for="confirmPassword">Confirm Password:</label>
    <input type="password" id="confirmPassword" name="confirmPassword" required>
    <span id="confirmPasswordError" class="error"></span>
    <br><br>
    <button type="submit">Submit</button>
  </form>
  <script>
    const form = document.getElementById('myForm');
    const passwordInput = document.getElementById('password');
    const confirmPasswordInput = document.getElementById('confirmPassword');
    const passwordError = document.getElementById('passwordError');
    const confirmPasswordError = document.getElementById('confirmPasswordError');
    function validatePassword() {
      if (!passwordInput.checkValidity()) {
        passwordError.textContent = passwordInput.validationMessage;
      } else {
        passwordError.textContent = '';
      }
    }
    function validateConfirmPassword() {
      if (confirmPasswordInput.value !== passwordInput.value) {
        confirmPasswordInput.setCustomValidity("Passwords do not match.");
      } else {
        confirmPasswordInput.setCustomValidity("");
      }
      confirmPasswordInput.reportValidity();
    }
    passwordInput.addEventListener('input', validatePassword);
    confirmPasswordInput.addEventListener('input', validateConfirmPassword);
    passwordInput.addEventListener('blur', validateConfirmPassword); //Also validate on blur, in case user doesn't type in confirm password
    confirmPasswordInput.addEventListener('blur', validatePassword); //Also validate on blur, in case user doesn't type in confirm password
    form.addEventListener('submit', function(event) {
      event.preventDefault(); // Prevent the default form submission
      validatePassword(); //Ensure latest validity
      validateConfirmPassword(); //Ensure latest validity
      if (!form.checkValidity()) {
        alert('Form has errors. Please correct them.');
      } else {
        alert('Form submitted successfully!');
      }
    });
  </script>
</body>
</html>
在这个示例中:
- 我们监听密码输入框和确认密码输入框的
input事件。 - 在
input事件处理程序中,我们使用checkValidity()方法检查密码的有效性,并使用setCustomValidity()方法检查确认密码是否与密码匹配。 - 我们还在
submit事件处理程序中再次调用验证函数,以确保在提交表单之前执行最新的验证。 
总结
novalidate属性提供了一种禁用原生表单验证的机制,同时允许我们使用约束验证API手动执行验证。这使我们可以自定义验证逻辑、自定义错误消息、进行实时验证,并处理复杂的交互。通过结合novalidate属性和约束验证API,我们可以创建更灵活、更用户友好的表单。掌握这些技术能够让你更好地控制表单验证流程,提供更佳的用户体验。