Let’s write our very own password validation. It will include features such as toggling password display, matching a confirm password field, and disabling form submission until the password is valid.
Client-side validation is one of the most important features that can be done with JavaScript. In fact, it’s pretty much why JavaScript was invented in the first place..
A Brief History of JavaScript
Before JavaScript, form validation could on be carried out only on the server side. This meant every time a user tried to submit a form, the information would be submitted to the server and if the server returned an error, the page would be reloaded to display the error message.
Back in the days of dial-up internet, this wasn’t quite the speedy process we have now and it meant having to make multiple calls to a server just so a user could fill out a login form or something of the sort. There was also no way to keep form data once the page reloaded, so users would often have to fill out forms multiple times.
Due to the tenuous nature of the whole process, a client-side scripting language was needed. The client side validation would serve as a middle man between the browser and the server by ensuring validity of form input before sending the information to the server, thus saving time and reducing the number of calls made to the server.
And that’s how JavaScript was born! For a more in-depth look into the history of JavaScript, check out my video on The Evolution of JavaScript.
Password Validation with JavaScript
Now that we know the purpose of JavaScript, let’s look into how we can apply it. Here’s what we’ll be building:
Let’s take a look at the features on our form to get an idea of what we’ll be building today:
- Password reveal button
- Password syntax validation
- Password confirmation
- Disabled button for invalid form
Like everything related to web design, user experience is an important consideration when building a feature and although there are several discussions around which method is best for password fields, we’ll be sticking with these features.
We use the password reveal button and password confirmation field so the user has the option of seeing what they’ve typed in the field and confirming if it’s correct. The password syntax validation text is listed clearly so the user knows what format the password should be before typing.
Now that we know what we’re working towards, let’s get started!
1. Form Markup with HTML
This tutorial uses the form from a previous tutorial so the base form styling and markup are explained there:
We’ll be adding some new markup elements to this form to recreate the layout in our demo so this is what our markup looks like now:
1 | <divclass="form-container"> |
2 | <formid="form"> |
3 | <divclass="input-container"> |
4 | <input |
5 | type="password" |
6 | id="password" |
7 | aria-describedby="requirements" |
8 | required |
9 | /> |
10 | <labelfor="password">Password</label> |
11 | <button |
12 | class="show-password" |
13 | id="show-password" |
14 | type="button" |
15 | role="switch" |
16 | aria-checked="false" |
17 | aria-label="Show password" |
18 | > |
19 | Show |
20 | </button> |
21 | </div> |
22 | |
23 | <divclass="password-requirements"> |
24 | <pclass="requirement"id="length">Min. 8 characters</p> |
25 | <pclass="requirement"id="lowercase">Include lowercase letter</p> |
26 | <pclass="requirement"id="uppercase">Include uppercase letter</p> |
27 | <pclass="requirement"id="number">Include number</p> |
28 | <pclass="requirement"id="characters">Include a special character: #.-?!@$%^&*</p> |
29 | </div> |
30 | |
31 | <divclass="input-container"> |
32 | <inputtype="password"id="confirm-password"required/> |
33 | <labelfor="confirm-password">Confirm password</label> |
34 | </div> |
35 | |
36 | <divclass="password-requirements"> |
37 | <pclass="requirement hidden error"id="match">Passwords must match</p> |
38 | </div> |
39 | |
40 | <divclass="submit-container"> |
41 | <inputtype="submit"id="submit"disabled/> |
42 | </div> |
43 | </form> |
44 | </div> |
We use the requirement elements with different ids under the password field to display the different password syntax requirements. We’ll target these elements later in our JavaScript to handle updating the password validity as the user types.



We also have an error requirement under the confirm password field which we’ll display only when the passwords don’t match.



The submit button is disabled by default and we’ll use an event listener in JavaScript to determine when it should be enabled.
There are also several accessibility considerations to keep in mind with this implementation. We need to make sure screen readers can also detect the updated password and submit fields. In this case, we’re using the following aria attributes to improve the accessibility of our form.
aria-describedby
: We assign the content of the password requirements field as the description of the input field to allow the information be read out by screen readers when the input is focused on.role="switch"
: We use this attribute to indicate that the button is a togglearia-checked
: This attribute is used together with therole="switch"
attribute to indicate whether the switch element is toggled on or offaria-label
: We use the aria-label to provide more descriptive text on our password toggle button for screen readers.
We’ll handle updating these attributes as needed in the JavaScript section. This article also shares more information on accessibility of password fields.
2. Styling Form Elements with CSS
We’ll use our styling to handle the different states of our submit button and password requirements. We’ll also apply some styles to the password toggle button to be displayed when the input field is being hovered or focused on.
1 | input[type=submit]{ |
2 | transition:.25s; |
3 | border-radius:4px; |
4 | border:1pxsolidrgba(0,0,0,0.12); |
5 | padding:16px; |
6 | background-color:white; |
7 | text-transform:uppercase; |
8 | letter-spacing:0.08em; |
9 | font-size:14px; |
10 | } |
11 | |
12 | input[type=submit]:disabled{ |
13 | color:#808080; |
14 | background-color:#f5f5f5; |
15 | cursor:not-allowed; |
16 | } |
17 | |
18 | input[type=submit]:not(:disabled):hover{ |
19 | border-color:transparent; |
20 | background-color:#6200ee24; |
21 | color:#6200ee; |
22 | } |
23 | |
24 | .show-password{ |
25 | transition:opacity.25s; |
26 | position:absolute; |
27 | background-color:transparent; |
28 | right:0; |
29 | margin:auto; |
30 | top:0; |
31 | bottom:0; |
32 | height:fit-content; |
33 | border:none; |
34 | font-size:10px; |
35 | color:grey; |
36 | cursor:pointer; |
37 | outline:none; |
38 | text-transform:uppercase; |
39 | } |
40 | |
41 | .show-password:hover, |
42 | .show-password:focus{ |
43 | color:black; |
44 | } |
45 | |
46 | .input-container:not(:hover,:focus-within).show-password{ |
47 | opacity:0; |
48 | } |
49 | |
50 | .password-requirements{ |
51 | display:flex; |
52 | flex-wrap:wrap; |
53 | margin-top:-1rem; |
54 | padding:016px; |
55 | } |
56 | |
57 | .requirement{ |
58 | font-size:14px; |
59 | flex:1050%; |
60 | min-width:max-content; |
61 | margin:2px0; |
62 | } |
63 | |
64 | .requirement:before{ |
65 | content:'\2639'; |
66 | padding-right:5px; |
67 | font-size:1.6em; |
68 | position:relative; |
69 | top:.15em; |
70 | } |
71 | |
72 | .requirement:not(.valid){ |
73 | color:#808080; |
74 | } |
75 | |
76 | .requirement.valid{ |
77 | color:#4CAF50; |
78 | } |
79 | |
80 | .requirement.valid:before{ |
81 | content:'\263A'; |
82 | } |
83 | |
84 | .requirement.error{ |
85 | color:red; |
86 | } |
87 |
3. Validation Functionality (JavaScript)
Now we can get down to writing code. Let’s start with the password toggle feature.
How to Build A Password Toggle with JavaScript
First, we’ll get the elements for our password fields and toggle button and store them as global variables.
1 | constpassword=document.getElementById("password"); |
2 | constconfirmPassword=document.getElementById("confirm-password"); |
3 | constshowPassword=document.getElementById("show-password") |
Once we have our elements, we can use event listeners to detect when the toggle has been clicked and update the fields accordingly.
In this case, when the password toggle is switched on, we want to change the input type
attribute from password
to text
as this is what allows the text in the password field to be visible. Alternatively, when the password toggle is switched off, we’ll change the password fields back to type password
. We also want to update the show password button text and aria values accordingly.
1 | showPassword.addEventListener('click',(event)=>{ |
2 | if(password.type=='password'){ |
3 | password.type="text" |
4 | confirmPassword.type="text" |
5 | showPassword.innerText='hide' |
6 | showPassword.setAttribute('aria-label','hide password') |
7 | showPassword.setAttribute('aria-checked','true') |
8 | }else{ |
9 | password.type="password" |
10 | confirmPassword.type="password" |
11 | showPassword.innerText='show' |
12 | showPassword.setAttribute('aria-label','show password') |
13 | showPassword.setAttribute('aria-checked','false') |
14 | } |
15 | }) |
How to Confirm Password with JavaScript
The next feature we’ll be working on is the password confirmation. With this implementation, we’ll add an event listener to the confirm password field to compare the values of the two fields once a user has finished typing.
To optimise performance, we can choose to either debounce this function or call it on blur.
Debouncing the confirmation function means we’ll be comparing the values when the user stops typing for a certain amount of time while blur means we’ll compare the values once the user removes focus from the confirm password field. Either implementation will suffice.
For this demo, we’ll be calling the function on blur but if you’d rather use the debounce function, this article explains more on how to implement that:
This is what the code looks like:
1 | constpassword=document.getElementById("password"); |
2 | constconfirmPassword=document.getElementById("confirm-password"); |
3 | constmatchPassword=document.getElementById("match"); |
4 | |
5 | confirmPassword.addEventListener("blur",(event)=>{ |
6 | constvalue=event.target.value |
7 | |
8 | if(value.length&&value!=password.value){ |
9 | matchPassword.classList.remove('hidden') |
10 | }else{ |
11 | matchPassword.classList.add('hidden') |
12 | } |
13 | }) |
If the value of the confirm password field is not empty and is not equal to the password field value on blur, then we display the password match error message.
We can also use a focus event listener to handle hiding the error message when the user starts typing again.
1 | confirmPassword.addEventListener("focus",(event)=>{ |
2 | matchPassword.classList.add('hidden') |
3 | }) |
How to Validate Password Syntax with JavaScript
Next, we want to handle validating the password as the user types it in. The password has to meet the following requirements:
- A minimum of 8 characters
- At least one uppercase letter
- At least one lowercase letter
- At least one number
- At least one special character from this list: #.-?!@$%^&*
We’ll need to define functions to handle each of these requirements, but first let’s define our general function to determine what happens when the requirement is valid or not.
If the requirement is valid, we add a class valid
to the requirement element display, and if it’s invalid, we remove the class. We’ll also need to target the specific requirement element using their id. This is what our function looks like:
1 | constupdateRequirement=(id,valid)=>{ |
2 | constrequirement=document.getElementById(id); |
3 | |
4 | if(valid){ |
5 | requirement.classList.add("valid"); |
6 | }else{ |
7 | requirement.classList.remove("valid"); |
8 | } |
9 | }; |
Now that we have our function to update the requirement, we’ll need to call this function for every requirement when our user types. Each requirement has a JavaScript function to check the condition:
To verify the minimum number of characters, we’ll check if the password length is greater than 8
1 | value.length>=8 |
To check if the password contains a lowercase letter, we’ll use the regex syntax:
1 | /[a-z]/.test(value) |
Let’s quickly break down the regex expression here as we’ll be using this for the other syntax validations:
//
: The slash is used to wrap a regex expression, similar to how a curly bracket is used to wrap a JavaScript function.[]
: The square bracket is used as an “any” condition for regex, this means it will return true if the value being tested contains any of the characters in the square bracket.a-z
: This expression returns a match for any characters between lowercase a and lowercase z..test()
: This method is used to match the value being tested to the regex expression.
To check if the password contains an uppercase letter:
1 | /[A-Z]/.test(value) |
To check if the password contains a number:
1 | /\d/.test(value) |
\d
: This is a regex character used to match any digit between 0-9. We could get the same result by using[0-9]
.
To check if the password contains special characters:
1 | /[#.?!@$%^&*-]/.test(value) |
We’ve gotten the conditions for all our password requirements so we can pass them into an input event listener on the password field to handle updating the corresponding requirement with its condition. This is what the code looks like:
1 | constpassword=document.getElementById("password"); |
2 | |
3 | password.addEventListener("input",(event)=>{ |
4 | constvalue=event.target.value; |
5 | |
6 | updateRequirement('length',value.length>=8) |
7 | updateRequirement('lowercase',/[a-z]/.test(value)) |
8 | updateRequirement('uppercase',/[A-Z]/.test(value)) |
9 | updateRequirement('number',/\d/.test(value)) |
10 | updateRequirement('characters',/[#.?!@$%^&*-]/.test(value)) |
11 | }); |
This is what our password validation form looks like now:



How to Disable a Submit Button with JavaScript
The last feature to implement is disabling the form button and preventing submission until the form is valid. In this case, our form is considered valid when the input in the password field meets all requirements and the confirm password input is the same as the password input.
Let’s handle confirming that the password meets all requirements. We can achieve this by combining the syntax logic into one function:
1 | value.length>=8&& |
2 | /[a-z]/.test(value)&& |
3 | /[A-Z]/.test(value)&& |
4 | /\d/.test(value)&& |
5 | /[#.?!@$%^&*-]/.test(value) |
Next we update our function to also check if the password fields match and if everything is in order, then we enable the submit button. This is what our function looks like:
1 | const handleFormValidation = () => { |
2 | const value = password.value; |
3 | const confirmValue = confirmPassword.value; |
4 | |
5 | if ( |
6 | value.length >= 8 && |
7 | /[a-z]/.test(value) && |
8 | /[A-Z]/.test(value) && |
9 | /\d/.test(value) && |
10 | /[#.?!@$%^&*-]/.test(value) && |
11 | value == confirmValue |
12 | ) { |
13 | submit.removeAttribute("disabled"); |
14 | return true; |
15 | } |
16 | |
17 | submit.setAttribute("disabled", true); |
18 | return false; |
19 | }; |
Finally, we use the change and submit event listeners on the form to detect if the form is valid before allowing the information be submitted.
1 | form.addEventListener("change",()=>{ |
2 | handleFormValidation(); |
3 | }); |
4 | |
5 | form.addEventListener("submit",(event)=>{ |
6 | event.preventDefault(); |
7 | constvalidForm=handleFormValidation(); |
8 | |
9 | if(!validForm){ |
10 | returnfalse; |
11 | } |
12 | |
13 | alert("Form submitted"); |
14 | }); |
Conclusion
And we’re done! Let's take a look at the final product again after all that code: