loading...

Built-in form validation

jordanholtdev profile image Jordan Holt Originally published at blog.jordanholt.dev on ・4 min read

There are many ways to validate user data when working with forms on the client-side. You can write custom JavaScript to do this, or use one of the many 3rd party libraries that are available like Formik for React. However, one of the easiest ways is to take advantage of the built-in validation in the HTML5 specs.

Let’s create a basic newsletter sign up form as an example. You can see the final form here.

One of the first ways to implement validation is by using the input element type attribute. When we include the type attribute some basic constraint validation and in some cases a value sanitization algorithm will be applied to the value of the form control.

For example, if we set the type attribute to “email”, the value attribute must be a valid email-address. If the value is not a valid email address then the browser will generate an error message for the user.

<p>
  <label
    >Email:
    <input type="email" />
  </label>
</p>
Enter fullscreen mode Exit fullscreen mode

However, the type="email" attribute in this case would still consider a value like “hello@b” as valid because it accepts intranet email addresses. So we could further constrain the validation by using the pattern attribute. The pattern attribute accepts a regular expression.

<p>
  <label
    >Email(required):
    <input
      type="email"
      pattern="^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$"
      title="example@example.com"
    />
  </label>
  An email address uses the following format: example@example.com
</p>
Enter fullscreen mode Exit fullscreen mode

You’ll notice that we also added a title attribute that is used to describe the pattern. Most browsers will display the title as a tooltip when hovered over the element, and will inlcude the title in the error message when the value does not match the pattern. However this isn’t always the case and it’s good practice to display that information in other ways to assist the user.

Another common attribute when using built-in validation is required. When this attribute is included on an input element, the field will mandatory and an error message will be displayed if the form is submitted and the input is empty.

<p>
  <label
    >Email(required):
    <input
      type="email"
      pattern="^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$"
      title="example@example.com"
      required
      placeholder="john@example.com"
      aria-required="true"
    />
  </label>
  An email address uses the following format: example@example.com
</p>
Enter fullscreen mode Exit fullscreen mode

Again, it’s a good idea to convey this visually to the user. In this case simply by adding “(required)” to the label. The aria-required attribute is set to true which is mostly redundant as current browsers often set this value to true when the required attribute is present. But to support more browsers it’s been included in this example. We also added a placeholder attribute to provide a hint to the user at the intended use of the data entry.

Let’s go ahead and add a field that collects the users name. This will be a required field that accepts the users name. We’ll add a length constraint because most names are going to contain only a small number of letters.

<p>
  <label
    >Name(required):
    <input
      type="text"
      pattern="^[a-zA-z]+[a-zA-z]{1,}"
      title="Name contains the characters a-z and is under 100 characters in lenth"
      required
      aria-required="true"
      maxlength="100"
      placeholder="John"
    />
  </label>
</p>
Enter fullscreen mode Exit fullscreen mode

Again, we’ve added the a pattern to this input element so only names containing the characters A-Z in upper of lower case will be valid. Be careful when writing your regular expressions as it’s easy to exclude people. For example if there are diacritic characters in a name the above pattern would not accept them as valid.

Now we could add a few styles to the form elements and take advantage of some of the UI pseudo-classes that are set when we use certain validation attributes. For example we can the :invalid pseudo-class to style the input elements when they do not meet the validation requirements.

form {
  padding: 1rem;
  font: 1em sans-serif;
  max-width: 320px;
}

form > h2 {
  line-height: 1.25;
  font-weight: 600;
  font-size: 1.5rem;
  color: rgb(26, 32, 44);
}

p > label {
  font-size: 1rem;
  padding-right: 12px;
  padding-bottom: 6px;
  opacity: 1;
  font-weight: 500;
  text-align: left;
  line-height: 1.25;
  display: block;
  border-width: 0px;
  border-style: solid;
  border-color: rgb(226, 232, 240);
}

input[type="text"],
input[type="email"],
fieldset {
  width: 100%;
  border: 1px solid #333;
  box-sizing: border-box;
  font-size: 1rem;
  padding-left: 1rem;
  padding-right: 1rem;
  height: 2.5rem;
  border-radius: 0.25rem;
  border-style: solid;
  border-color: inherit;
  background-color: rgb(255, 255, 255);
}

input:focus {
  z-index: 1;
  border-color: rgb(49, 130, 206);
  box-shadow: rgb(49, 130, 206) 0px 0px 0px 1px;
}

input:invalid {
  background-color: rgba(255, 0, 0, 0.301);
}

input:valid {
  background-color: rgba(0, 128, 0, 0.301);
}

form > p > button {
  margin-top: 1rem;
  height: 2.5rem;
  min-width: 2.5rem;
  background-color: rgb(49, 151, 149);
  color: rgb(255, 255, 255);
  font-size: 1rem;
  padding-left: 1rem;
  padding-right: 1rem;
  font-weight: 600;
  border: 1px solid black;
  border-radius: 0.25rem;
}

form > p > button:hover {
  background-color: rgb(44, 122, 123);
  cursor: pointer;
}
Enter fullscreen mode Exit fullscreen mode

Wrap up

With just a few simple attributes we can easily start to implement client-side validation when working with forms. While this won’t be as flexible as using JavaScript it can provide a simple solution. Remember that you should always be using thorough server-side validation and sanitization in addition to client-side validation.

Discussion

pic
Editor guide