Validating forms using constraint validation API

Validating forms using constraint validation API

Some words about validation

  • client side form validation ensures safety for submitted data.
  • an initial check it an important feature and a good user experience.
  • both client and server need to have validation so we don’t get any inconsitency beteen submitting data.

Differences between HTML5 form validation and JavaScript validation

  • HTML5 form validation dosen’t require much javascript and it has better performance than javascript.
  • JavaScript validation is much more customizable, but you need to create it all.

Today we are going to validate our form using the constraint validation API and display custom messages for validation errors. Let’s get started:

1. HTML5 form:

  <form novalidate>
      <div class="form-row">
        <div class="form-group col-md-6">
          <label for="username">Username</label>
          <input type="text" id="username" class="form-control" required />
          <span class="error" aria-live="polite"></span>
        </div>
      </div>
      <div class="form-row">
        <div class="form-group col-md-6">
          <label for="email">Email</label>
          <input
            type="email"
            class="form-control"
            required
            id="email"
            minlength="8"
          />
          <span class="error" aria-live="polite"></span>
        </div>
      </div>
      <button type="submit" class="btn btn-primary">Sign in</button>
    </form>

Two inputs that need validation:

  • email input
  • username input

Form valid when all of the following criteria are matched:

  • username has a value
  • email has a value
  • an correct email is provided
  • length bigger than a defined value

The following span is used for display validation errors:

  <span class="error" aria-live="polite"></span>

As you might seen above, we are using two ordinary inputs and two span elements for validation inputs.

2. Styling input element for valid or invalid pseudo-class:

  • valid input element triggers :valid css pseudo-class
  • invalid input element triggers :invalid css pseudo-class

Depending on which validator you use, corresponding pseudo-class will be activated for that particular element.

CSS style

input:invalid {
  border-color: #200;
  background-color: #fdd;
}

input:valid {
  background-color: rgb(0, 206, 0);
  color: #fff;
}

input:focus:invalid {
  outline: none;
}

3. JavaScript

Query for input elements:

const form = document.getElementsByTagName("form")[0];

const email = document.getElementById("email");
const username = document.getElementById("username");
const emailError = document.querySelector("#email + span.error");
const usernameError = document.querySelector("#username + span.error");

Attach input events for both email and username:

addInputEventListener.call(email, "input", emailError, showError);
addInputEventListener.call(username, "input", usernameError, showError);

Handle input event for both email and username by using a single function.

function addInputEventListener(onEvent, errorElement, errorCallback) {
  this.addEventListener(onEvent, ev => {
    if (this.validity.valid) {
      errorElement.className = "";
      errorElement.innerHTML = "";
    } else {
      errorCallback(this.name);
    }
  });
}

Add the handler for submitting the form:

  form.addEventListener("submit", function(event) {
    showError("email");
    showError("username");
    event.preventDefault();
  });

After everything is set, we have to implement our customized error messages:

function buildErrorMessage(element, errorMessagesMap) {
  const errors = [];
  for (const prop in element.validity) {
    if (element.validity[prop]) {
      errors.push(errorMessagesMap[prop]);
    }
  }
  return { element, errors };
}

function setErrorMessage({ element, errors }) {
  element.innerHTML = errors.join("<br/> ");
}
function errorOrEmptyClass({ element, errors }) {
  element.className = errors.length > 0 ? "error active" : "";
}

function showError(name) {
  const nameToErrorInput = {
    email: emailError,
    username: usernameError
  };
  const validator = {
    username: element => ({
      ...buildErrorMessage(username, {
        valueMissing: "Username is required."
      }),
      element
    }),
    email: element => ({
      ...buildErrorMessage(email, {
        valueMissing: "Email is required.",
        typeMismatch: "Value needs to be an email address.",
        tooShort: `Email to short you need ${
          email.minLength
        } characters. You entered ${email.value.length}.`
      }),
      element
    })
  };
  [validator[name](nameToErrorInput[name])].map(setErrorMessage);
  [validator[name](nameToErrorInput[name])].map(errorOrEmptyClass);
}

Summary

HTML form elements implement the following validation interface.

The Constraint validation API helps us by providing the validity property and other useful methods, but we’ve done our validation just by using the validity property.

validity property contains getters for checking exact which aspect of validation has just failed.

Example:

  • username element has a required validation:
  • valueMissing getter will return true when no value is provided.
  • username.validity.valueMissing // true

Stay tuned to get more tutorials on Web development

Follow me on twitter