Tuesday, July 19, 2022

JavaScript Form Validation (Practical Tutorial)

JavaScript Form Validation (Practical Tutorial)

JavaScript is a big help when accepting data in public-facing forms on websites; it enables us to ensure data entered is accurate before being submitted to a server.

What we’ll be creating

In this tutorial, we’ll build a simple login form and add front-end validation with vanilla JavaScript. The overarching goal is to provide helpful feedback to the end-user and ensure the submitted data is what we want!

info
This tutorial is written so that beginners can follow along—but some basic JavaScript knowledge and understanding of coding principles will certainly help!

Live Demo of Our JavaScript Form Validation

Check out the pen below to test our JavaScript form validation—fork it, feel free to copy snippets of the code, and follow the tutorial to see how it was created!

Why Modern JavaScript Works Well Enough

Thanks to the recent advancements in JavaScript, we can confidently use modern JavaScript to accomplish form validations without any dependencies.

However, some frameworks out in the wild simplify the validation strategy. We won’t be using any dependencies for this tutorial to make everything function.

It’s also worth mentioning in many situations, HTML5 has built-in validations that might work well enough for your projects. These can be handy if you aren’t interested in creating your custom validations or lack the extra time. This tutorial will leverage the custom route to make the experience more branded.

1. Add the Markup

Starting with the HTML, I created a simple account creation form displayed in the center of a page. The form features four input fields (username, email, password, password confirmation) and a submit input button.

Within the markup, we’ll add some additional elements to provide validation feedback to the end-user. These include some SVG icons (sourced from heroicons.com and some empty <span> tags. These are to provide actionable instructions if a field isn’t passing a validation.

Things to note at this point:

  • Each input field is grouped in a div element with a class of input-group. We’ll use CSS to add some space between each field. Within each grouping, we have a set of SVG icons, a span field, and an input field. We will use SVG icons to provide a visual cue whether the input is valid or invalid.
  • We’ll use CSS to initially hide each icon from view. Then we can leverage JavaScript to hide and show them relative to the user’s input.
  • I also plan to display a helpful message within the span tag with a class of error-message should the form’s validations be triggered to execute. There is a single span tag per input grouping.
  • Finally, notice that each input has an id assigned. The id attribute is vital for the tutorial’s JavaScript portion.

2. Styling the Form With CSS

To make the form much easier to use and more accessible, we’ll add some CSS.

I linked to a Google Fonts Specimen called Inter (which you may recognize from Tuts+). It’s a great sans-serif font face adaptable to many use cases.

If you’re using CodePen and following along, you can find our font linked in the head tag options inside the HTML pane of the editor.

Codepen.io HTML head element contents in screenshotCodepen.io HTML head element contents in screenshotCodepen.io HTML head element contents in screenshot

The CSS is relatively straightforward. We are using CSS to hide all icons on the initial load, and we’ll be toggling the state of them with JavaScript coming up.

3. Plugging in the JavaScript

Now on to the feature presentation! Adding JavaScript is what is going to make or break this form.

I mentioned a few goals before, but here is the entire list in an outline:

  • Ensure the correct data is entered into each field.
  • Display helpful feedback if a field is valid or invalid
  • Don’t allow the form to be submitted if any fields are invalid
  • Validate the form in real-time as a user types or clicks Create account.

Thinking Modularly

With the onset of ES6 and further support of JavaScript in modern browsers, we can leverage some newer features that allow the code we write to be more reusable.

With that in mind, I’ll be using JavaScript’s constructor pattern and create a new Class that can be used for future forms if necessary.

These patterns stem from backend programming concepts, though, in JavaScript, it’s more of extraction since JavaScript isn’t a traditional programming language. I’d recommend reading more about the inner-workings of classes and constructors if you want to understand more behind how they work.

I should mention that the final code isn’t 100% modular, but the idea is to shift it that way as much as possible. As your application or website scales, it might make sense to entertain the idea of a JavaScript framework since frameworks tend to extract repeated logic. These frames of thought allow you to reuse code and keep things more organized.

Start With a New Class

To kick things off, we’ll create a new class called FormValidator.

We’ll add a constructor function inside the class and accept two arguments. You can think of this function as the one you get for free with any class in JavaScript. The idea is to be able to call the class later on somewhere else and pass in arguments for reuse. That might not be 100% clear yet, but as we progress, it should make more sense.

We’ll initialize new values to instances in the FormValidator class inside the constructor function. These values allow us to use them anywhere within the scope of the class, thanks to the this keyword. In this case, this refers to the scoped class FormValidator, but this can always change based on its scope.

The constructor function initializes a form and the fields within the form. My goal is to have the FormValidator extract the logic away, so all we have to do is pass references to a given form and its field by their identifiers or names. That will make more sense in a bit.

Target Form Elements

Next up, I’ll create some variables that query the DOM for the elements we will target. Those, of course, include the form and its fields.

With those in place, we can set up a new instance of our FormValidator class.

The form variable is responsible for querying the DOM to find the form element on the page with a class of .form. The fields variable is an array of names referenced by each form field’s id attribute. The names in your HTML must match the contents of the array values for proper targeting. We’ll use the array values inside the FormValidator class to query for each field as necessary.

The accountForm variable is our reference to a new instance of the FormValidator class. We pass in the form and fields variables which initializes those inside the class for use wherever we need them.

At this point, nothing gets called from the class, so we need to add some more code to kick that action off.

I added an initialize function to the FormValidator class below the constructor function. You can name this function whatever you wish, but I’ll commonly use initialize as a preference.

Inside the initialize function, I wrote a simple console.log() statement and passed in the values we set up within the constructor function. If all goes well, this should log the instance of our form and the array of field ids I mentioned prior.

At the end of the file, I’m calling the initialize() function directly.

That should log something like the following in your browser's console.

console log statement for accountForm.initialize methodconsole log statement for accountForm.initialize methodconsole log statement for accountForm.initialize method

Success! We know the code so far is accurate thanks to those values outputting what we expect.

4. Add Form Validations on User Input

Listening to a user’s input will help us preemptively validate each field. We can hook into the JavaScript addEventListener() method to do just that. You can listen to various events, and in this section, we’ll pay attention to the input event specifically.

I’ll make a new function in the class called validateOnEntry().

A lot in these few lines of code is likely confusing. Let me explain in greater detail.

Understanding Scope

First, we create a variable called self and assign it to this.

Setting this variable up acts as a way to target the value of this relative to the scope of the overarching class (FormValidator) from within other nested scopes.

The reason I included the self variable is so we can have access to the core class FormValidator inside the addEventListener() method since the scope changes if you nest any code inside the function. Read more about Scope on MDN.

Looping Through

To properly validate each field, we’ll need to loop through each array property inside the fields variable we set up using the constructor function. The code outputs each field name we want to target using the forEach() method.

During the loop, we use backticks to dynamically query the document (DOM) to find the appropriate field identified by the id attribute.

Finally, we use each dynamic field, assign it to an input variable and call the addEventListener() function. We listen for an input event and call a new function we’ll create named validateFields. This function accepts a single input for validation purposes.

I chose to extract the validation logic into a new function called validateFields() because much of the code is reusable. Removing the code to a new function also aids in legibility from a developer’s point of view.

5. Validating Each Field

To validate the form fields, we’ll need some conditional statements along with some bells and whistles to make the interface react in real-time.

Before we write the logic for the validateFields function, I’ll write another function responsible for the design portion. We can reuse this function later, so it makes sense to extract it to a single set of logic, which I called setStatus.

The setStatus function accepts three parameters: the field we are targeting, a message if needed, and the validation status.

Inside the function, we begin with three variables. successIcon, errorIcon, and errorMessage.

If you recall, each form grouping has a set of these elements in the markup. Two are SVG icons, and the other is an empty span that takes responsibility for displaying text content if validation fails. The field parameter will be how each repeated icon and span tag is targeted relative to its positioning in the DOM.

Below the variables are two conditional statements that check for string status values we’ll add to the validateFields function.

One statement checks for "success" and the other for "error" state denoted by a status parameter that gets passed through to the setStatus function.

Within each conditional, you’ll find more logic that toggles icon classes and resets the error messages to any message passed through to the setStatus function. The logic in this code is all happening in real-time as a user types into a field.

Ensuring Fields Aren’t Empty

With the setStatus function authored, we can now put it to use by performing the validations on each field. Depending on your forms, you may require unique validations if you have individual form fields. Maybe you don’t want any fields to be blank, for example.

We’ll start with that goal and ensure each field isn’t blank.

The code above takes the field argument and targets its value. Using the trim() method in JavaScript, we can remove any white spaces and check if the value is an empty string.

If the input is empty, we’ll use the setStatus function and pass the field, the query statement to find the .error-message span tag relative to the field, and the status of "error".

If the input is not empty, we can use the same setStatus function within the FormValidator class to display a success state. No message is necessary for this state so we can pass null for the message argument.

Ensuring an Email Address is Valid

When creating your own JavaScript form validation, checking for valid email addresses is an art in itself! Here’s how we’ll go about it:

Firstly, we’ll make sure the field type defined with the JavaScript API is an email type.

We’ll then leverage some REGEX patterns to ensure the email entered matches our expectations. The test() method allows you to pass in a REGEX pattern to return a boolean (true or false value) by “testing” it against what value is given.

If the value makes the cut, we’ll again use our setStatus functions to display feedback to the user. We can customize the message value to whatever makes sense for the validation.

Password Confirmation

Last but not least is the password confirmation validation. The goal is to ensure the password field matches the password confirmation field, and we’ll do this by comparing both values.

We need to go one step further to validate multiple edge cases for the password confirmation field. The password confirmation, of course, can’t be a blank field, and we also need to ensure it matches the password input’s field value.

For each case, we display the appropriate status.

6. Validation on Submit

Our JavaScript form validation is almost complete! But we have yet to account for the submit button, a critical piece of the form itself. We’ll need to repeat the process we did for the input event for the submit event using another addEventListener() function.

That will come from another function I’ll call validateOnSubmit().

In the validateOnSubmit() function we’ll target the form instance we set up on the constructor function previously. The form gives us access to the event listener type known as submit since those elements are tied together in HTML.

Using an addEventListener() function, we’ll listen for the submit event and pass the event through to the function’s body.

Inside the function body we can use the preventDefault() method to keep the form from submitting in its default manner. We want to do this to prevent any nasty data from passing if validation is not passing.

We’ll again set a self variable assigned to this so we have access to the higher level of scope in our FormValidator class.

With this variable, we can loop through the fields instance initialized within the FormValidator class. That gets performed from within the addEventListener() function.

Each field we loop through is assigned to an input variable and finally passed through the validateFields function we created previously.

A lot is happening here, but luckily we can reuse a lot of code from before to accomplish the same goals!

Clicking the Create account button ensures each field is valid before making it through.

7. Calling the Validations

The last piece of the JavaScript form validation puzzle is calling both the validateOnEntry() and validateOnSubmit() functions. If you recall, we called the initialize() function at the beginning of this tutorial. I’ll use it to call the two functions.

The Final Result

With all our validations and functions in place, here’s the final JavaScript form validation code for reference. Much of this code is reusable, and you can always add additional field types.

A Word of Warning!

If a user toggles off JavaScript in their browser, you risk letting insufficient data into your website or application. This problem is the downside to using only front-end form validation.

I would advise adding a fallback solution for form validations using something like backend code.

I write a lot of applications using front-end and backend code using a web application framework like Ruby on Rails. The framework handles a lot of these problems for me along with enhanced security features.

Even if I add front-end validation, I’ll almost always take the extra initiative to add backend validations to an application or website.

Adding backend validations ensures that if JavaScript happens to be disabled in a browser or maybe a fluke incident occurs, I can still depend on the backend code (typically on a server) to keep insufficient data out.

Closing Thoughts

While there are many enhancements we can make to this sample account creation form, I hope the approaches taken in this tutorial have shed some light on ways you can enhance your own forms with simple JavaScript validation.

Remember, front-end validations are only part of the piece of properly validating form submission data.

A backend solution or some intermediary step to filter the data is a significant barrier to keeping the nasty data from reaching wherever you store your data. Happy coding!

Learn More Front End JavaScript

We have a growing library of front-end JavaScript tutorials on Tuts+ to help you with your learning:


No comments:

Post a Comment