JavaScript Form Validation: Validate Inputs Before Submission

JavaScript form validation checks user input before your website accepts or sends a form.

You can check whether required fields are empty, whether an email address has a valid format, whether a password meets your rules, or whether a user accepted required terms.

Good form validation should help users fix mistakes clearly. It should not make forms harder to complete.

Course Home: JavaScript Tutorial for Beginners
Previous Lesson: JavaScript Events Explained
Next Lesson: JavaScript Fetch API Explained

Quick Answer

A common JavaScript form-validation pattern is:

<form id="signupForm">
    <label for="email">Email</label>
    <input id="email" type="email">

    <p id="emailError"></p>

    <button type="submit">Sign Up</button>
</form>
const signupForm = document.querySelector("#signupForm");
const email = document.querySelector("#email");
const emailError = document.querySelector("#emailError");

signupForm.addEventListener("submit", (event) => {
    if (email.value.trim() === "") {
        event.preventDefault();

        emailError.textContent = "Email is required";
    }
});

The form listens for the submit event.

If the email is empty, JavaScript:

  1. Stops the submission.
  2. Shows an error message.
  3. Lets the user correct the field.

For important forms, client-side validation should also be repeated on the server. Browser JavaScript can improve the user experience, but users can bypass it.

What Is Form Validation in JavaScript?

Form validation checks whether user input meets the rules your form requires.

For example, a signup form may require:

  • A name
  • An email address
  • A password
  • A minimum password length
  • Agreement to terms

A checkout form may require:

  • Delivery address
  • City
  • Postal code
  • Phone number
  • Payment-related information

JavaScript can read the current values and decide whether the form is ready to continue.

Why Form Validation Matters

Validation helps prevent incomplete or clearly incorrect form submissions.

It can also give users immediate feedback.

For example:

Email is required

is more useful than submitting the form and showing a vague error later.

Useful validation can:

  • Point to the field that needs attention
  • Explain what is wrong
  • Keep valid user input in place
  • Reduce avoidable submission errors
  • Improve form completion
  • Help users correct mistakes before sending data

Validation should be clear, specific, and easy to recover from.

HTML Validation Comes First

Before writing JavaScript, use the validation features already available in HTML.

Example:

<form>
    <label for="email">Email</label>

    <input
        id="email"
        name="email"
        type="email"
        required
    >

    <button type="submit">Submit</button>
</form>

This uses:

type="email"
required

The browser can already check whether the field is empty and whether the value resembles an email address.

JavaScript should enhance useful HTML validation rather than replace it without reason.

Useful HTML Validation Attributes

Common attributes include:

AttributePurpose
requiredField must contain a value
minlengthMinimum text length
maxlengthMaximum text length
minMinimum numeric or date value
maxMaximum numeric or date value
patternValue must match a pattern
type="email"Email-format validation
type="url"URL-format validation

Example:

<input
    id="password"
    type="password"
    required
    minlength="8"
>

The browser now knows the password field is required and needs at least eight characters.

JavaScript Validation and HTML Validation Work Together

You might use HTML for basic rules:

<input
    id="email"
    type="email"
    required
>

Then use JavaScript for custom feedback:

if (email.value.trim() === "") {
    emailError.textContent = "Enter your email address";
}

This gives you more control over how errors appear while keeping useful browser behavior.

Basic Form Setup

Use this form for the first examples:

<form id="signupForm" novalidate>
    <div>
        <label for="name">Name</label>
        <input id="name" name="name" type="text">
        <p id="nameError"></p>
    </div>

    <div>
        <label for="email">Email</label>
        <input id="email" name="email" type="email">
        <p id="emailError"></p>
    </div>

    <button type="submit">Sign Up</button>
</form>

The novalidate attribute disables the browser’s default validation messages for this example.

That lets JavaScript display custom messages.

Do not add novalidate automatically to every real form. Use it only when your JavaScript provides a complete and accessible validation experience.

Listen for the Form submit Event

Select the form:

const signupForm = document.querySelector("#signupForm");

Then listen for submission:

signupForm.addEventListener("submit", (event) => {
    console.log("Form submitted");
});

Use the form’s submit event rather than relying only on a button click.

A form can also be submitted through keyboard actions such as pressing Enter.

You learned this behavior in the JavaScript Events tutorial.

Stop Submission When Validation Fails

Use:

event.preventDefault();

when your JavaScript finds an error.

Example:

signupForm.addEventListener("submit", (event) => {
    if (email.value.trim() === "") {
        event.preventDefault();
    }
});

Do not prevent submission automatically before you know whether the form is valid unless your application intentionally handles the entire submission process with JavaScript.

Validate a Required Text Field

HTML:

<label for="name">Name</label>
<input id="name" type="text">

<p id="nameError"></p>

JavaScript:

const nameInput = document.querySelector("#name");
const nameError = document.querySelector("#nameError");

function validateName() {
    const name = nameInput.value.trim();

    if (name === "") {
        nameError.textContent = "Name is required";
        return false;
    }

    nameError.textContent = "";
    return true;
}

The function returns:

false

when validation fails.

It returns:

true

when the name is acceptable.

Why Use trim()?

A user could enter only spaces:

"     "

Without trim(), that string is not empty.

Example:

const value = "     ";

console.log(value === "");

Output:

false

But:

console.log(value.trim() === "");

Output:

true

For many required text fields, trimming before the empty check is useful.

Validate an Email Field

HTML:

<label for="email">Email</label>

<input
    id="email"
    type="email"
>

<p id="emailError"></p>

JavaScript:

const emailInput = document.querySelector("#email");
const emailError = document.querySelector("#emailError");

function validateEmail() {
    const email = emailInput.value.trim();

    if (email === "") {
        emailError.textContent = "Email is required";
        return false;
    }

    if (!emailInput.validity.valid) {
        emailError.textContent = "Enter a valid email address";
        return false;
    }

    emailError.textContent = "";
    return true;
}

This uses the browser’s built-in email validity check instead of trying to create a complicated email regular expression.

Why Avoid Overly Complex Email Regex?

Email-address rules are more complicated than they appear.

A large custom regular expression can reject valid addresses or accept values you did not expect.

For normal browser forms, this is often a better starting point:

<input type="email">

Then JavaScript can inspect the field’s validity.

Validation checks whether the format is acceptable for the form. It does not prove that the email address exists or belongs to the user.

Email ownership normally requires a verification process such as sending a confirmation message.

The validity Property

Form controls expose a:

validity

property.

Example:

console.log(emailInput.validity.valid);

This returns:

true

or:

false

depending on whether the field satisfies its active HTML constraints.

The validity object contains more detailed flags.

Common ValidityState Properties

Useful properties include:

valid
valueMissing
typeMismatch
tooShort
tooLong
rangeUnderflow
rangeOverflow
patternMismatch
customError

Example:

if (emailInput.validity.valueMissing) {
    console.log("Email is missing");
}

Another:

if (emailInput.validity.typeMismatch) {
    console.log("Email format is invalid");
}

These checks can help you show a more specific message.

Use required With the Constraint Validation API

HTML:

<input
    id="email"
    type="email"
    required
>

JavaScript:

if (emailInput.validity.valueMissing) {
    emailError.textContent = "Email is required";
} else if (emailInput.validity.typeMismatch) {
    emailError.textContent = "Enter a valid email address";
}

This combines HTML constraints with custom JavaScript feedback.

checkValidity()

You can ask whether a form control currently satisfies its constraints:

const isValid = emailInput.checkValidity();

console.log(isValid);

The result is a boolean.

You can also call:

form.checkValidity();

to check all controls participating in constraint validation.

reportValidity()

reportValidity() checks constraints and can also ask the browser to display its validation UI.

Example:

if (!signupForm.reportValidity()) {
    console.log("Form has validation errors");
}

Use this when you want native browser feedback.

If you provide fully custom error messages, you may use your own validation flow instead.

setCustomValidity()

You can give a field a custom validation error.

Example:

const username = document.querySelector("#username");

username.setCustomValidity("This username is not available");

The field now fails constraint validation.

Clear the custom error when the issue is resolved:

username.setCustomValidity("");

An empty string removes the custom validity error.

Real Website Example: Matching Passwords

HTML:

<label for="password">Password</label>
<input id="password" type="password">

<label for="confirmPassword">Confirm Password</label>
<input id="confirmPassword" type="password">

<p id="passwordError"></p>

JavaScript:

const password = document.querySelector("#password");
const confirmPassword = document.querySelector("#confirmPassword");
const passwordError = document.querySelector("#passwordError");

function validatePasswordMatch() {
    if (password.value !== confirmPassword.value) {
        passwordError.textContent = "Passwords do not match";
        return false;
    }

    passwordError.textContent = "";
    return true;
}

This is a practical example of comparing two form values.

Validate Password Length

Suppose your form requires at least eight characters.

HTML:

<input
    id="password"
    type="password"
    minlength="8"
    required
>

JavaScript:

function validatePassword() {
    if (password.validity.valueMissing) {
        passwordError.textContent = "Password is required";
        return false;
    }

    if (password.validity.tooShort) {
        passwordError.textContent =
            "Password must contain at least 8 characters";

        return false;
    }

    passwordError.textContent = "";
    return true;
}

Keep password requirements clear and visible before submission rather than revealing them only after an error.

Do Not Treat Frontend Password Rules as Security

Client-side validation can check whether a password meets your interface rules.

It cannot safely enforce account security by itself.

The server must validate and process passwords securely.

Never trust browser JavaScript as your only security control.

Validate a Checkbox

HTML:

<label>
    <input id="terms" type="checkbox">
    I accept the terms
</label>

<p id="termsError"></p>

JavaScript:

const terms = document.querySelector("#terms");
const termsError = document.querySelector("#termsError");

function validateTerms() {
    if (!terms.checked) {
        termsError.textContent = "Accept the terms to continue";
        return false;
    }

    termsError.textContent = "";
    return true;
}

Checkboxes use:

checked

rather than the text value for a simple checked-state test.

HTML Can Validate a Required Checkbox

You can also use:

<input
    id="terms"
    type="checkbox"
    required
>

Then:

terms.validity.valueMissing

will indicate whether the required checkbox is unchecked.

Validate a Select Menu

HTML:

<label for="country">Country</label>

<select id="country">
    <option value="">Choose a country</option>
    <option value="india">India</option>
    <option value="usa">United States</option>
</select>

<p id="countryError"></p>

JavaScript:

const country = document.querySelector("#country");
const countryError = document.querySelector("#countryError");

function validateCountry() {
    if (country.value === "") {
        countryError.textContent = "Choose a country";
        return false;
    }

    countryError.textContent = "";
    return true;
}

The placeholder option uses an empty value so it is easy to identify.

Validate a Number Range

HTML:

<label for="quantity">Quantity</label>

<input
    id="quantity"
    type="number"
    min="1"
    max="5"
    required
>

<p id="quantityError"></p>

JavaScript:

const quantity = document.querySelector("#quantity");
const quantityError = document.querySelector("#quantityError");

function validateQuantity() {
    if (quantity.validity.valueMissing) {
        quantityError.textContent = "Choose a quantity";
        return false;
    }

    if (
        quantity.validity.rangeUnderflow ||
        quantity.validity.rangeOverflow
    ) {
        quantityError.textContent =
            "Quantity must be between 1 and 5";

        return false;
    }

    quantityError.textContent = "";
    return true;
}

Using min and max keeps the rules visible in the HTML.

Validate With pattern

HTML supports the pattern attribute for certain text-like inputs.

Example:

<input
    id="pinCode"
    type="text"
    inputmode="numeric"
    pattern="[0-9]{6}"
    required
>

JavaScript can check:

pinCode.validity.patternMismatch

Use a pattern only when the rule is clear and appropriate for your users.

Do not force one format onto data that genuinely varies.

Real Website Example: Six-Digit PIN Code

const pinCode = document.querySelector("#pinCode");
const pinCodeError = document.querySelector("#pinCodeError");

function validatePinCode() {
    if (pinCode.validity.valueMissing) {
        pinCodeError.textContent = "PIN code is required";
        return false;
    }

    if (pinCode.validity.patternMismatch) {
        pinCodeError.textContent = "Enter a 6-digit PIN code";
        return false;
    }

    pinCodeError.textContent = "";
    return true;
}

This example uses the HTML pattern as the validation rule.

Combine Several Validation Functions

Suppose you have:

validateName()
validateEmail()
validatePassword()
validateTerms()

Your submit handler can run them:

signupForm.addEventListener("submit", (event) => {
    const nameIsValid = validateName();
    const emailIsValid = validateEmail();
    const passwordIsValid = validatePassword();
    const termsAreValid = validateTerms();

    const formIsValid =
        nameIsValid &&
        emailIsValid &&
        passwordIsValid &&
        termsAreValid;

    if (!formIsValid) {
        event.preventDefault();
    }
});

This keeps each field’s rules in a focused function.

Why Store Each Validation Result?

Do not write:

if (
    !validateName() ||
    !validateEmail() ||
    !validatePassword()
) {
    event.preventDefault();
}

when your validation functions need to run for every field.

Logical OR short-circuiting can stop after the first truthy failure condition.

Storing each result separately ensures every validation function runs.

That lets the page show all relevant errors at once.

You learned short-circuit behavior in the JavaScript Logical Operators tutorial.

Complete Basic Validation Example

HTML:

<form id="signupForm" novalidate>
    <div>
        <label for="name">Name</label>

        <input
            id="name"
            name="name"
            type="text"
            required
        >

        <p id="nameError"></p>
    </div>

    <div>
        <label for="email">Email</label>

        <input
            id="email"
            name="email"
            type="email"
            required
        >

        <p id="emailError"></p>
    </div>

    <div>
        <label for="password">Password</label>

        <input
            id="password"
            name="password"
            type="password"
            minlength="8"
            required
        >

        <p id="passwordError"></p>
    </div>

    <label>
        <input
            id="terms"
            name="terms"
            type="checkbox"
            required
        >
        I accept the terms
    </label>

    <p id="termsError"></p>

    <button type="submit">Create Account</button>
</form>

JavaScript:

const signupForm = document.querySelector("#signupForm");

const nameInput = document.querySelector("#name");
const emailInput = document.querySelector("#email");
const passwordInput = document.querySelector("#password");
const termsInput = document.querySelector("#terms");

const nameError = document.querySelector("#nameError");
const emailError = document.querySelector("#emailError");
const passwordError = document.querySelector("#passwordError");
const termsError = document.querySelector("#termsError");

function validateName() {
    if (nameInput.value.trim() === "") {
        nameError.textContent = "Name is required";
        return false;
    }

    nameError.textContent = "";
    return true;
}

function validateEmail() {
    if (emailInput.validity.valueMissing) {
        emailError.textContent = "Email is required";
        return false;
    }

    if (emailInput.validity.typeMismatch) {
        emailError.textContent = "Enter a valid email address";
        return false;
    }

    emailError.textContent = "";
    return true;
}

function validatePassword() {
    if (passwordInput.validity.valueMissing) {
        passwordError.textContent = "Password is required";
        return false;
    }

    if (passwordInput.validity.tooShort) {
        passwordError.textContent =
            "Password must contain at least 8 characters";

        return false;
    }

    passwordError.textContent = "";
    return true;
}

function validateTerms() {
    if (!termsInput.checked) {
        termsError.textContent = "Accept the terms to continue";
        return false;
    }

    termsError.textContent = "";
    return true;
}

signupForm.addEventListener("submit", (event) => {
    const nameIsValid = validateName();
    const emailIsValid = validateEmail();
    const passwordIsValid = validatePassword();
    const termsAreValid = validateTerms();

    const formIsValid =
        nameIsValid &&
        emailIsValid &&
        passwordIsValid &&
        termsAreValid;

    if (!formIsValid) {
        event.preventDefault();
    }
});

This example validates all four fields before allowing normal form submission.

Validate While the User Types

You can also validate after an input event.

Example:

nameInput.addEventListener("input", () => {
    validateName();
});

For email:

emailInput.addEventListener("input", () => {
    validateEmail();
});

Real-time validation can help, but avoid showing aggressive errors before the user has had a reasonable chance to enter the value.

A common approach is:

  1. Validate on submit.
  2. After a field has shown an error, update that error while the user fixes it.

Validate on blur

You can check a field when the user leaves it:

emailInput.addEventListener("blur", () => {
    validateEmail();
});

This can provide earlier feedback than waiting for submission.

However, validation timing should not create a distracting stream of error messages.

Clear an Error While the User Fixes It

Example:

emailInput.addEventListener("input", () => {
    if (emailInput.validity.valid) {
        emailError.textContent = "";
    }
});

This removes the error once the field satisfies its current HTML constraints.

Add an Error Class

CSS:

.field-error {
    border-color: currentColor;
}

JavaScript:

if (!emailInput.validity.valid) {
    emailInput.classList.add("field-error");
}

Remove it when valid:

emailInput.classList.remove("field-error");

Do not communicate errors through color alone.

Always provide text explaining what needs to be fixed.

Add aria-invalid

For an invalid form control, you can set:

emailInput.setAttribute("aria-invalid", "true");

When the field becomes valid:

emailInput.removeAttribute("aria-invalid");

This can help assistive technology understand the current error state.

Use accessibility attributes accurately rather than adding them without updating their state.

Connect an Error Message With aria-describedby

HTML:

<label for="email">Email</label>

<input
    id="email"
    type="email"
    aria-describedby="emailError"
>

<p id="emailError"></p>

The aria-describedby relationship tells assistive technology that the error paragraph provides additional information about the field.

If the same element also contains persistent help text, keep that relationship useful even when no error is present.

Focus the First Invalid Field

When submission fails, moving focus to the first invalid field can help users find the problem.

Example:

signupForm.addEventListener("submit", (event) => {
    if (!signupForm.checkValidity()) {
        event.preventDefault();

        const firstInvalidField =
            signupForm.querySelector(":invalid");

        firstInvalidField?.focus();
    }
});

If you use novalidate, your own validation logic must correctly identify invalid controls.

Do not move focus repeatedly while the user is typing.

Use :invalid Carefully

CSS can target invalid form controls:

input:invalid {
    border-color: currentColor;
}

But required fields may be considered invalid before the user interacts with them.

That can make a fresh form look full of errors.

Many interfaces add a class only after submission or after a field has been touched, then style invalid fields within that state.

Custom Validation Message Helper

If several fields need similar error handling, create a helper function.

function showError(input, errorElement, message) {
    errorElement.textContent = message;
    input.setAttribute("aria-invalid", "true");
}

And:

function clearError(input, errorElement) {
    errorElement.textContent = "";
    input.removeAttribute("aria-invalid");
}

Now your field functions can focus on validation rules.

Example:

function validateName() {
    if (nameInput.value.trim() === "") {
        showError(
            nameInput,
            nameError,
            "Name is required"
        );

        return false;
    }

    clearError(nameInput, nameError);
    return true;
}

This reduces repeated DOM code.

Some rules involve more than one field.

A password confirmation is one example.

HTML:

<input id="password" type="password">
<input id="confirmPassword" type="password">

JavaScript:

function validatePasswordMatch() {
    if (password.value !== confirmPassword.value) {
        showError(
            confirmPassword,
            confirmPasswordError,
            "Passwords do not match"
        );

        return false;
    }

    clearError(confirmPassword, confirmPasswordError);
    return true;
}

The error belongs most naturally to the confirmation field because that is where the mismatch is being resolved.

Real Website Example: Contact Form

HTML:

<form id="contactForm" novalidate>
    <label for="contactName">Name</label>
    <input
        id="contactName"
        type="text"
        required
    >

    <p id="contactNameError"></p>

    <label for="contactEmail">Email</label>
    <input
        id="contactEmail"
        type="email"
        required
    >

    <p id="contactEmailError"></p>

    <label for="message">Message</label>
    <textarea
        id="message"
        minlength="20"
        required
    ></textarea>

    <p id="messageError"></p>

    <button type="submit">Send Message</button>
</form>

The message field can be checked with:

function validateMessage() {
    if (messageInput.validity.valueMissing) {
        messageError.textContent = "Message is required";
        return false;
    }

    if (messageInput.validity.tooShort) {
        messageError.textContent =
            "Message must contain at least 20 characters";

        return false;
    }

    messageError.textContent = "";
    return true;
}

The same validation structure can support many forms.

Real Website Example: Shopping Quantity

HTML:

<input
    id="quantity"
    type="number"
    min="1"
    max="10"
    required
>

JavaScript:

function validateQuantity() {
    if (!quantityInput.checkValidity()) {
        quantityError.textContent =
            "Choose a quantity from 1 to 10";

        return false;
    }

    quantityError.textContent = "";
    return true;
}

The browser handles the numeric range rules defined in HTML.

Real Website Example: Delivery Option

HTML:

<label>
    <input
        type="radio"
        name="delivery"
        value="standard"
    >
    Standard
</label>

<label>
    <input
        type="radio"
        name="delivery"
        value="express"
    >
    Express
</label>

<p id="deliveryError"></p>

JavaScript:

function validateDelivery() {
    const selectedDelivery =
        document.querySelector(
            'input[name="delivery"]:checked'
        );

    if (!selectedDelivery) {
        deliveryError.textContent =
            "Choose a delivery option";

        return false;
    }

    deliveryError.textContent = "";
    return true;
}

The selector finds the checked radio button.

If none is selected, it returns null.

Read Form Values With FormData

The browser provides FormData for reading successful form controls.

HTML:

<form id="profileForm">
    <input name="name" value="Riya">
    <input name="city" value="Delhi">
</form>

JavaScript:

const profileForm = document.querySelector("#profileForm");

const formData = new FormData(profileForm);

console.log(formData.get("name"));
console.log(formData.get("city"));

Output:

Riya
Delhi

FormData becomes especially useful when you later send form data with the Fetch API.

Validate Before Creating FormData for Submission

A useful flow is:

form.addEventListener("submit", (event) => {
    if (!form.checkValidity()) {
        event.preventDefault();
        return;
    }

    const formData = new FormData(form);
});

If JavaScript is sending the request itself, you will usually prevent the normal submission and then send the validated data.

You will learn that in the JavaScript Fetch API tutorial.

Never Trust Client-Side Validation Alone

This is one of the most important rules in form development.

JavaScript runs in the user’s browser.

A user can:

  • Disable JavaScript
  • Change the HTML
  • Send a request manually
  • Modify values before submission
  • Bypass your interface completely

For this reason:

Client-side validation improves usability. Server-side validation protects your application and data.

Every important rule must be checked again on the server.

Client-Side vs Server-Side Validation

Client-side validation

Runs in the browser.

Useful for:

  • Fast feedback
  • Required-field messages
  • Format guidance
  • Range checks
  • Better form usability

Server-side validation

Runs on your server.

Required for:

  • Security
  • Data integrity
  • Authorization
  • Business rules
  • Trusted validation
  • Safe database operations

A production application normally needs both.

Validation Is Not Sanitization

Validation checks whether data matches your rules.

For example:

Is this field empty?
Is this number between 1 and 5?
Does this email field have an acceptable format?

Sanitization and output encoding solve different problems.

Do not assume a value is safe to insert into HTML just because it passed a form validation rule.

When displaying user-provided text in the DOM, prefer:

textContent

instead of injecting untrusted strings with:

innerHTML

Review the JavaScript DOM Manipulation tutorial for this distinction.

Do Not Validate by Disabling Paste

Do not block paste in password, email, or confirmation fields simply to force manual typing.

Users may rely on password managers, clipboard tools, or accessibility workflows.

Validate the resulting value rather than restricting normal input methods without a strong reason.

Avoid Unclear Error Messages

Weak:

Invalid input

Better:

Enter your email address

Or:

Password must contain at least 8 characters

Error messages should tell the user what needs fixing.

Keep Error Messages Near the Field

Place field-specific errors close to the related control.

Example:

<label for="email">Email</label>
<input id="email" type="email">

<p id="emailError"></p>

This is easier to understand than placing every error only at the top of a long form.

For long forms, you may also provide an error summary that links users to each invalid field.

Do Not Clear User Input After Validation Fails

If a user enters a long form and one field is wrong, keep their valid values.

Do not reset the entire form simply because one field failed validation.

Make recovery easy.

Do Not Show Errors Before They Are Useful

A blank required field is technically invalid before the user enters anything.

But showing:

Name is required

the moment the page loads can feel like the form is already blaming the user.

Common validation timing options include:

  • On submit
  • On blur after interaction
  • While editing a field that already has an error

Choose timing that helps rather than distracts.

Prevent Double Submission

After a valid JavaScript-controlled submission begins, you may temporarily disable the submit button.

Example:

submitButton.disabled = true;

Then restore it if the request fails:

submitButton.disabled = false;

Do this only when your JavaScript controls the asynchronous submission flow.

You will handle that properly after learning fetch() and async/await.

Common Beginner Mistakes

Validating Only the Submit Button Click

Avoid relying only on:

submitButton.addEventListener("click", validateForm);

Listen for:

form.addEventListener("submit", validateForm);

because the form can be submitted in other ways.

Calling preventDefault() Without Need

If the form is valid and should use normal browser submission, do not prevent it.

Use preventDefault() when validation fails or when JavaScript intentionally handles the submission.

Forgetting trim() on Required Text Fields

A string containing spaces can pass a simple:

value !== ""

check.

For many required text fields, use:

value.trim() !== ""

Using Only a Huge Email Regex

For standard browser forms, start with:

type="email"

and the browser’s constraint-validation tools.

Do not assume a complicated regular expression proves an email address is real.

Trusting Client-Side Validation for Security

Browser validation can be bypassed.

Validate important data again on the server.

Showing Errors With Color Only

A red border by itself does not explain the problem.

Add a clear text message.

Forgetting to Clear Old Errors

If a field becomes valid, remove its old error message.

Otherwise the page may show stale information.

Using innerHTML for User Input

Avoid:

message.innerHTML = userInput;

for untrusted form values.

Use:

message.textContent = userInput;

when displaying plain text.

Comparing Number Inputs as Strings Without Thinking

Input values are generally read as strings.

Example:

console.log(quantityInput.value);

may return:

"5"

For numeric logic, you can use:

quantityInput.valueAsNumber

with suitable number inputs.

Example:

const quantity = quantityInput.valueAsNumber;

Check for missing or invalid values before using the number.

Assuming required Handles Every Business Rule

required only tells you a value must be present.

It does not check every rule your application may need.

For example, a username may be syntactically valid but already taken.

That kind of rule may require a server request.

Showing All Errors Only at the End of the Page

Users should be able to connect an error to the field that needs correction.

Place field errors near the controls and use an error summary when helpful for long forms.

Resetting the Form After an Error

Do not erase valid input just because one field failed.

Blocking Submission Without Explaining Why

Every blocked submission should provide clear feedback.

The user should know what to fix.

Best Practices for JavaScript Form Validation

Use semantic HTML labels and native form controls.

Start with HTML validation attributes.

Use JavaScript for custom rules and clearer feedback.

Listen for the form’s submit event.

Use trim() for required free-text fields when surrounding spaces should not count.

Use validity, checkValidity(), and related browser APIs where they fit.

Return clear booleans from focused validation functions.

Keep field-specific validation in small functions.

Show errors close to the related fields.

Use text, not color alone, to explain errors.

Set aria-invalid accurately when providing custom validation states.

Keep valid user input after errors.

Focus the first invalid field after a failed submission when that improves usability.

Do not show errors too early.

Validate important data again on the server.

Do not treat validation as sanitization or security.

Beginner Exercise

Use this HTML:

<form id="loginForm" novalidate>
    <label for="email">Email</label>

    <input
        id="email"
        type="email"
        required
    >

    <p id="emailError"></p>

    <label for="password">Password</label>

    <input
        id="password"
        type="password"
        minlength="8"
        required
    >

    <p id="passwordError"></p>

    <button type="submit">Login</button>
</form>

Complete these tasks:

  1. Listen for the submit event.
  2. Show Email is required when email is empty.
  3. Show Enter a valid email address when its format fails.
  4. Show Password is required when password is empty.
  5. Show Password must contain at least 8 characters when too short.
  6. Prevent submission when either field is invalid.
  7. Clear each error after that field becomes valid.

Challenge Exercise

Create a signup form containing:

  • Name
  • Email
  • Password
  • Confirm password
  • Terms checkbox

Validation rules:

  1. Name cannot be empty.
  2. Email must pass the browser’s email validation.
  3. Password must contain at least eight characters.
  4. Confirm password must match the password.
  5. Terms must be checked.

Create one validation function for each rule.

Run all validation functions on form submission.

Prevent submission when any rule fails.

Extra Challenge

Add:

aria-invalid

to invalid fields.

Remove it when each field becomes valid.

Then focus the first invalid field after a failed submission.

Frequently Asked Questions

What is JavaScript form validation?

JavaScript form validation checks user input against rules before or during form submission and can show custom feedback when values are missing or invalid.

Should I validate forms with HTML or JavaScript?

Use both where appropriate.

HTML provides useful native constraints such as required, type="email", minlength, min, max, and pattern.

JavaScript can add custom rules, custom messages, and dynamic behavior.

What is the submit event?

The submit event fires on a form when the form is submitted.

For validation, listen on the form:

form.addEventListener("submit", handleSubmit);

What does preventDefault() do in form validation?

It stops the form’s normal submission behavior when your JavaScript needs to keep the user on the page, such as after validation fails.

How do I check whether an input is empty?

For many text inputs:

input.value.trim() === ""

is a useful required-field check.

How do I validate an email in JavaScript?

For standard browser forms, use:

<input type="email">

and check the control’s validity.

Example:

emailInput.validity.valid

Do not rely on a complicated regular expression to prove an email address exists.

How do I validate a password length?

Use HTML:

minlength="8"

and JavaScript when you need custom messages.

You can inspect:

password.validity.tooShort

How do I check whether two passwords match?

Compare their values with strict equality:

password.value === confirmPassword.value

How do I validate a checkbox?

Check its:

checked

property.

Example:

if (!terms.checked) {
    // show error
}

How do I validate a select menu?

Check its selected value.

Example:

if (country.value === "") {
    // show error
}

What is the Constraint Validation API?

It is a browser API for working with form validation rules.

Useful features include:

validity
checkValidity()
reportValidity()
setCustomValidity()

What does checkValidity() do?

It checks whether an element or form satisfies its active HTML validation constraints and returns a boolean.

What does reportValidity() do?

It checks validity and can display the browser’s validation feedback when a control is invalid.

What does setCustomValidity() do?

It adds a custom validity error message to a form control.

Clear it with:

input.setCustomValidity("");

What is validity.valueMissing?

It becomes true when a required form control has no acceptable value.

What is validity.typeMismatch?

It can become true when a value does not match the input type, such as an invalid value in an email input.

What is validity.tooShort?

It indicates that a text value is shorter than its minlength requirement when the browser applies that constraint.

Is JavaScript form validation secure?

Not by itself.

Client-side validation can be bypassed.

Important rules must also be validated on the server.

Is validation the same as sanitization?

No.

Validation checks whether data follows rules.

Sanitization, safe output handling, and encoding address different concerns.

Should I use innerHTML to show form values?

Not for untrusted user input.

Use textContent when displaying plain user-provided text.

Should validation happen while the user types?

It can, but avoid showing aggressive errors too early.

A useful pattern is to validate on submit, then update existing errors while the user corrects the fields.

Should I disable the submit button until the form is valid?

Sometimes, but it can hide why submission is unavailable.

If you disable it, make the requirements clear and ensure the form remains usable.

Allowing submission and showing specific errors is often easier for users.

Why should server-side validation repeat browser validation?

Because users can bypass or modify browser-side JavaScript and HTML.

The server must independently decide whether submitted data is acceptable.

What should I learn after JavaScript form validation?

Learn the JavaScript Fetch API next. It lets your code send and receive data without requiring a traditional full-page navigation.

Summary

JavaScript form validation checks whether form values meet your rules before they are accepted or sent.

Start with useful HTML constraints such as:

required
type="email"
minlength
maxlength
min
max
pattern

Then use JavaScript when you need custom behavior.

You learned how to:

  • Listen for form submission
  • Use preventDefault()
  • Check required fields
  • Use trim()
  • Validate email fields
  • Validate password length
  • Compare password fields
  • Validate checkboxes
  • Validate select menus
  • Check number ranges
  • Use validity
  • Use checkValidity()
  • Use reportValidity()
  • Use setCustomValidity()
  • Show custom error messages
  • Clear old errors
  • Add aria-invalid
  • Focus an invalid field
  • Read data with FormData
  • Keep validation accessible
  • Separate client-side validation from server-side security

Good validation helps users correct mistakes quickly.

It should never be your only protection for important application data.

Continue Learning JavaScript

Previous Lesson: JavaScript Events Explained

Course Home: JavaScript Tutorial for Beginners

Next Lesson: JavaScript Fetch API Explained

In the next lesson, you will learn how fetch() sends HTTP requests, reads JSON responses, handles errors, and connects your JavaScript forms and pages to APIs.

James Parker

James Parker

James Parker is a technology writer and WordPress enthusiast who shares practical guides on SEO, web development, AI tools, and digital growth. He focuses on creating easy-to-follow tutorials that help beginners and professionals build better websites and improve their online presence.