Skip to content
Laviyx Learn

Try

JavaScript course menu

Handling Forms with JavaScript: submit, preventDefault, FormData and Validation

Learn to handle HTML forms with JavaScript: listening for submit, preventing page reloads, reading values with input.value and FormData, checkboxes and selects, input and change events, custom validation messages with the Constraint Validation API, and resetting forms.

Intermediate8 min readUpdated
On this page

Forms collect input; JavaScript can read that input, check it, show feedback, and send it to a server without reloading the page. If you have not met HTML forms yet, read HTML Forms first.

Listening for submit#

Always listen for the form's submit event, not a button's click. submit fires whether the user clicks the button or presses Enter in a field:

HTML
<form id="signup">
  <p><label>Name <input name="name" required></label></p>
  <p><label>Email <input name="email" type="email" required></label></p>
  <button type="submit">Sign up</button>
</form>
<p id="out"></p>
JavaScript
const form = document.querySelector("#signup");

form.addEventListener("submit", (event) => {
  event.preventDefault();   // stop the page from reloading
  const name = form.elements.name.value.trim();
  document.querySelector("#out").textContent = `Thanks, ${name}! Check your email.`;
  form.reset();
});
Result
  • event.preventDefault() stops the browser's normal submission, which would load a new page.
  • The browser's built-in validation (required, type="email") still runs before the submit event, so the handler only runs with valid data.
  • form.elements.name finds the field with name="name"; .value is its current text (always a string).
  • form.reset() restores all fields to their starting values.

Reading all fields with FormData#

FormData collects every named field at once:

HTML
<form id="order">
  <p><label>Item <select name="item"><option>Tea</option><option>Coffee</option></select></label></p>
  <p><label>Quantity <input name="qty" type="number" value="1" min="1"></label></p>
  <p><label><input type="checkbox" name="sugar" checked> Sugar</label></p>
  <p>
    <label><input type="radio" name="size" value="small" checked> Small</label>
    <label><input type="radio" name="size" value="large"> Large</label>
  </p>
  <button>Order</button>
</form>
<pre id="out"></pre>
JavaScript
const form = document.querySelector("#order");

form.addEventListener("submit", (event) => {
  event.preventDefault();
  const data = Object.fromEntries(new FormData(form));
  data.qty = Number(data.qty);
  data.sugar = data.sugar === "on";
  document.querySelector("#out").textContent = JSON.stringify(data, null, 2);
});
Result
  • new FormData(form) reads all fields that have a name.
  • Object.fromEntries turns it into a plain object.
  • Values are strings: convert numbers with Number(). An unticked checkbox is missing entirely, and a ticked one gives "on" unless it has a value.
  • For fields with several values (several checkboxes with the same name), use formData.getAll("name").

A FormData object can also be sent directly with fetch: fetch("/api/order", { method: "POST", body: new FormData(form) }).

Reacting as the user types#

EventFires when
inputThe value changes, on every keystroke, paste or selection
changeThe user commits a change: leaves a text field, or picks a select option or checkbox
focus / blurThe field gains or loses focus
HTML
<label>Password <input id="pwd" type="password"></label>
<ul id="rules">
  <li data-rule="length">At least 8 characters</li>
  <li data-rule="digit">Contains a number</li>
</ul>
JavaScript
const pwd = document.querySelector("#pwd");
const checks = {
  length: (v) => v.length >= 8,
  digit: (v) => /\d/.test(v),
};

pwd.addEventListener("input", () => {
  for (const li of document.querySelectorAll("#rules li")) {
    const ok = checks[li.dataset.rule](pwd.value);
    li.style.color = ok ? "seagreen" : "";
    li.textContent = li.textContent.replace(/^✓ /, "");
    if (ok) li.textContent = "✓ " + li.textContent;
  }
});
Result

Custom validation messages#

The Constraint Validation API lets you add your own rules while keeping the browser's validation UI:

HTML
<form id="reg">
  <p><label>Password <input id="p1" type="password" required minlength="6"></label></p>
  <p><label>Confirm <input id="p2" type="password" required></label></p>
  <button>Register</button>
</form>
<p id="out"></p>
JavaScript
const form = document.querySelector("#reg");
const p1 = document.querySelector("#p1");
const p2 = document.querySelector("#p2");

function checkMatch() {
  p2.setCustomValidity(p1.value === p2.value ? "" : "Passwords do not match");
}
p1.addEventListener("input", checkMatch);
p2.addEventListener("input", checkMatch);

form.addEventListener("submit", (event) => {
  event.preventDefault();
  document.querySelector("#out").textContent = "Registered!";
});
Result
  • setCustomValidity("message") marks the field invalid with your message; setCustomValidity("") marks it valid again.
  • form.checkValidity() returns whether every field is valid; form.reportValidity() also shows the messages.

Client-side checks are for the user's convenience. The server must always validate the data again.

Accessible error messages#

When you show your own error text instead of the browser's, connect it to the field so screen readers announce it:

HTML
<input id="phone" aria-describedby="phone-error" aria-invalid="true">
<p id="phone-error">Enter a 10-digit mobile number.</p>

Practice#

Practice exercise

Build a "split the bill" form with an amount field and a number-of-people field (minimum 1). On submit, show each person's share with two decimals, without reloading the page.

Show solution
HTML
<form id="split">
  <p><label>Bill amount (₹) <input name="amount" type="number" min="0" step="0.01" required></label></p>
  <p><label>People <input name="people" type="number" min="1" value="2" required></label></p>
  <button>Split</button>
</form>
<p id="out"></p>
JavaScript
const form = document.querySelector("#split");
form.addEventListener("submit", (event) => {
  event.preventDefault();
  const { amount, people } = Object.fromEntries(new FormData(form));
  const share = Number(amount) / Number(people);
  document.querySelector("#out").textContent = `Each person pays ₹${share.toFixed(2)}`;
});
Result

Key takeaway#

Finished this lesson?