Real-Time Form Validation in Vanilla JavaScript
Form validation is a fundamental part of building user-friendly and secure web applications. While libraries like jQuery used to be the go-to solution, modern JavaScript makes it easier than ever to implement sleek, real-time validation directly in the browser without any dependencies. In this article, we’ll explore how to build dynamic form input validation using clean, efficient vanilla JavaScript.
1. Setting Up the HTML Form
Let’s begin with a simple HTML form containing an email field, phone number input, and password field. This will serve as our foundation for adding real-time validation logic via JavaScript.
<form id="signup-form" novalidate>
<label>
Email:
<input type="email" id="email" name="email" required />
<span class="error" id="email-error"></span>
</label>
<br />
<label>
Phone:
<input type="tel" id="phone" name="phone" required />
<span class="error" id="phone-error"></span>
</label>
<br />
<label>
Password:
<input type="password" id="password" name="password" required />
<span class="error" id="password-error"></span>
</label>
<br />
<button type="submit">Sign Up</button>
</form>
Each field is associated with a <span> element to display validation error messages. The novalidate attribute disables native browser validation so we have full control over the logic.
2. Listening to Input Events
To enable real-time validation, we attach input event listeners to watch each field as the user types. This allows us to give feedback immediately rather than waiting until form submission.
document.addEventListener("DOMContentLoaded", () => {
const emailInput = document.getElementById("email");
const phoneInput = document.getElementById("phone");
const passwordInput = document.getElementById("password");
emailInput.addEventListener("input", validateEmail);
phoneInput.addEventListener("input", validatePhone);
passwordInput.addEventListener("input", validatePassword);
});
This pattern scales easily—just bind functions to each field and handle logic individually. This keeps code clean and modular.
3. Implementing Email Validation
Let’s build the validateEmail function. We’ll use a regular expression to check for typical email formats and display an error message if the input is invalid.
function validateEmail(event) {
const email = event.target.value;
const errorSpan = document.getElementById("email-error");
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!regex.test(email)) {
errorSpan.textContent = "Please enter a valid email address.";
} else {
errorSpan.textContent = "";
}
}
The regex pattern used is a basic yet effective way to catch most invalid email formats. Be cautious about over-complicating regex for emails—too strict can frustrate users.
4. Validating Phone Numbers
Phone number formats vary by country, but for general UX, we often ask for 10 digits without symbols. Here’s how you might apply that:
function validatePhone(event) {
const phone = event.target.value;
const errorSpan = document.getElementById("phone-error");
const regex = /^\d{10}$/; // Adjust as needed for other formats
if (!regex.test(phone)) {
errorSpan.textContent = "Enter a 10-digit phone number.";
} else {
errorSpan.textContent = "";
}
}
For even better UX, you could strip out non-digit characters automatically or guide the user with input masks, but this simple example illustrates the basics.
5. Real-Time Password Strength Checker
A good password validation encourages secure practices. Here’s how we can check for a minimum length, one uppercase letter, one number, and one special character.
function validatePassword(event) {
const password = event.target.value;
const errorSpan = document.getElementById("password-error");
const regex = /^(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()_+])[A-Za-z\d!@#$%^&*()_+]{8,}$/;
if (!regex.test(password)) {
errorSpan.textContent =
"Password must be at least 8 chars, include uppercase, number, and special char.";
} else {
errorSpan.textContent = "";
}
}
Feedback appears instantly and helps guide the user toward meeting the security criteria without guesswork. You can enhance this further with visual indicators like progress bars or color cues.
6. Blocking Form Submission on Errors
Even with real-time validation, you should prevent form submission if the inputs are invalid. Here’s how to combine validation checks at the submit event level:
document.getElementById("signup-form").addEventListener("submit", function (e) {
validateEmail({ target: email });
validatePhone({ target: phone });
validatePassword({ target: password });
const hasErrors = document.querySelectorAll(".error").some((span) => span.textContent !== "");
if (hasErrors) {
e.preventDefault();
alert("Please correct the errors before submitting the form.");
}
});
Here, we run all three validation functions again on submission and prevent default behavior if any error messages exist.
7. Performance & Best Practices
- Debounce input events: Use
setTimeoutto reduce validation logic runs on high-frequency typing. - Keep your validation logic modular: One function per field keeps things testable.
- Use custom validity where needed: The
setCustomValidity()API can tie into the browser’s native validation styling. - Accessibility counts: Use
aria-liveregions or screen-reader friendly error alerts.
Conclusion
Real-time form validation in vanilla JavaScript is fully achievable and extremely effective for building better user experiences. With modular code, regex expressions, and event-driven architecture, you can both improve form accuracy and enhance usability. Skip the bloat of external libraries when plain JavaScript now offers all the power needed to build dynamic validations from scratch.
Useful links:


