⚡IntermediateAdvanced Forms

novalidate and Custom Validation

Disable native browser validation with novalidate and implement custom logic using the Constraint Validation API.

Overview

The novalidate attribute on <form> disables native browser validation entirely, giving you full control over how validation errors are displayed. Combined with the Constraint Validation API, you can use HTML's built-in validity checks (required, pattern, minlength, etc.) in JavaScript while presenting errors in your own styled UI. Key Constraint Validation API methods and properties: - input.validity: a ValidityState object with specific flags (valueMissing, typeMismatch, patternMismatch, etc.) - input.checkValidity(): returns true/false, fires 'invalid' event if false - input.reportValidity(): same as checkValidity() but also shows the browser's built-in bubble - input.setCustomValidity("message"): sets a custom error; empty string clears it

Key Points

  • •novalidate on <form> disables all native browser popup validation
  • •Constraint Validation API still works with novalidate you read validity in JS
  • •input.validity.valueMissing is true when a required field is empty
  • •input.validity.typeMismatch is true when the value doesn't match the type (e.g., bad email)
  • •input.setCustomValidity("") clears a custom error; any non-empty string sets one
  • •The invalid event fires on each invalid field when form submission is attempted

Examples

novalidate with custom error display

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

  <div class="field">
    <label for="pass">Password (8+ chars)</label>
    <input type="password" id="pass" name="password" required minlength="8">
    <span class="error" aria-live="polite"></span>
  </div>

  <button type="submit">Sign Up</button>
</form>

<script>
const form = document.getElementById('signup');

form.addEventListener('submit', (e) => {
  e.preventDefault();
  let valid = true;

  form.querySelectorAll('input').forEach(input => {
    const errorSpan = input.nextElementSibling;
    if (!input.checkValidity()) {
      valid = false;
      errorSpan.textContent = input.validationMessage;
      input.setAttribute('aria-invalid', 'true');
    } else {
      errorSpan.textContent = '';
      input.removeAttribute('aria-invalid');
    }
  });

  if (valid) form.submit();
});
</script>

Custom validity with setCustomValidity

<form novalidate>
  <label for="username">Username</label>
  <input type="text" id="username" name="username" required>

  <label for="confirm">Confirm Username</label>
  <input type="text" id="confirm" name="confirm_username">

  <button type="submit">Submit</button>
</form>

<script>
const username = document.getElementById('username');
const confirm  = document.getElementById('confirm');

confirm.addEventListener('input', () => {
  if (confirm.value !== username.value) {
    confirm.setCustomValidity('Usernames do not match.');
  } else {
    confirm.setCustomValidity(''); // Clear error
  }
});
</script>

setCustomValidity("") must be called to clear a previously set error.

Reading specific validity flags

const input = document.getElementById('email');
input.addEventListener('blur', () => {
  const v = input.validity;
  if (v.valueMissing)    showError('Email is required.');
  else if (v.typeMismatch) showError('Please enter a valid email address.');
  else if (v.tooShort)   showError(`Min ${input.minLength} characters required.`);
  else                   clearError();
});

Common Mistakes

  • Calling setCustomValidity("message") but never calling setCustomValidity("") when valid the error persists forever
  • Using novalidate without implementing any JS validation forms submit with invalid data
  • Not associating error messages with inputs via aria-describedby or aria-live for screen readers

Note

The best pattern: use novalidate + the Constraint Validation API (validity flags + setCustomValidity) so you get the benefit of HTML5's built-in rules while controlling the visual presentation completely.

Exercises

Write HTML to solve each exercise. Use Preview to see it render in a live iframe, then mark as done.

Loading editor…

Completed: 0 / 5 exercises

Knowledge Check

Question 1 of 4

What does the novalidate attribute on <form> do?