Tiffany B. Brown

23 Mar 2014Comments Off

HTML5: Determining which validation constraint triggered an error

When working with custom error messages and HTML5 forms, it helps to know which validation constraint rule was broken so that we can provide a tailored error message.

When a form field’s input is invalid, the browser fires an invalid event on that field. Let’s say that we have the following form with a required email field.

<form action="/handler" method="post">
    <p>
        <label for="email">E-mail address:</label>
        <input type="email" id="email" name="email" required>
        <button type="submit">Submit</button>
    </p>
</form>

When this form is submitted, capable browsers will check whether the value of the email field meets the constraints of the input field’s type. Since it’s also a required field, its value will pass the validation test only if:

  • the user entered a value; and
  • the value entered matches the email address pattern.

If it fails to meet either constraint, the browser will fire an invalid event that we can listen for

var email = document.querySelector("#email");

email.addEventListener('invalid', function(e){
  e.target.setCustomValidity('Double-check what you entered in the e-mail field.');
});

A message like the one above works, but leaves some ambiguity about what’s causing the error. How do you, the developer, determine which validation constraint wasn’t met?

Here’s where the validity IDL attribute comes in.

The validity attribute returns a ValidityState object. It contains the following properties:

  • badInput
  • customError
  • patternMismatch
  • rangeOverflow
  • rangeUnderflow
  • stepMismatch
  • tooLong
  • typeMismatch
  • valueMissing

Each of these properties has a value of true or false depending on which constraint the input fails to meet. Not all of these properties apply to every input type, however. For example, input.validity.rangeOverflow, will never be true for a text field.

In addition to the ones above, the ValidityState object also contains a valid property that indicates whether the field is valid or not, but not what kind of error occurred.

In this case, a mistyped email address — such as foo#example.com — would make the value of email.validity.typeMatch equal to true. All other properties would be false.

A missing email address, on the other hand, would cause email.validity.valueMissing to have a value of true, while all other properties would have a value of false.

We can check the value of these properties within our invalid event handler, and display a message that explains specifically what’s wrong with the input (or lack thereof).

var email = document.querySelector("#email");

email.addEventListener('invalid', function(e){
    var msg;
    if(e.target.validity.valueMissing){
        msg = 'Your email address is missing';
    }
    if(e.target.validity.typeMismatch){
        msg = 'Your email address is invalid';
    }
    e.target.setCustomValidity(msg);
});

View a working example on CodePen.

Let’s look at another example, this time using a pattern. We’ll create a text field for U.S. postal codes (ZIP Codes).

<form action="/handler" method="post">
    <p>
        <label for="zip">ZIP code</label>
        <input type="text" id="zip" name="zip" maxlength="5" size="5" minlength="5" pattern="[0-9]{5}" required>
        <button type="submit">Submit</button>
    </p>
</form>

ZIP Codes are five digit numeric strings. We are using a very loose pattern here for the pattern attribute. We’re just checking that our input contains five digits ranging from 0 through 9. Matching actual ZIP Codes is a bit more complicated, but this is good enough for this example.

Our invalid event handler looks much the same, but this time, we’ll check the value of the patternMismatch property.

var zip = document.querySelector("#zip");

zip.addEventListener('invalid', function(e){
    var msg;
    if(e.target.validity.valueMissing){
        msg = 'We need your ZIP code. Please include it.';
    }
    if(e.target.validity.patternMismatch){
        msg = 'Your ZIP code should contain exactly five digits.';
    }
    e.target.setCustomValidity(msg);
});

Working example on CodePen.

Once these invalid fields meet the validation constraints, the user will be able to submit the form.

Previous entries