Forms are where users give you something valuable: an enquiry, an order, a sign-up. Good client-side validation catches mistakes early and explains them clearly, so people finish the form. In this lesson you will read form data with jQuery, add jQuery validation that runs when the form is submitted and as fields are left, and display error messages that work for keyboard and screen-reader users too. The HTML side is covered in HTML Forms.
Reading form values
| Field type | How to read it |
|---|---|
| Text, email, number, textarea | $('#email').val() – always a string; add .trim() to remove spaces |
| Select | $('#city').val() – the selected option’s value |
| Checkbox | $('#terms').prop('checked') – true or false |
| Radio group | $('input[name="size"]:checked').val() – undefined if none is chosen |
| Several checkboxes | $('input[name="topics"]:checked').map((i, el) => el.value).get() – an array |
| Whole form | $('#form').serialize() – a name=value&… string; .serializeArray() – an array of { name, value } |
.serialize() produces exactly what a normal form submission would send, so it is perfect for $.post() from the AJAX lesson. Only fields with a name attribute are included, and unticked checkboxes and disabled fields are left out, just as in a real submission.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>serialize(), checkboxes and a disabled button</title>
<style>
body { font-family: system-ui, sans-serif; padding: 16px; line-height: 1.5; }
fieldset { margin: 8px 0; }
code { background: #f3f4f6; padding: 2px 4px; }
</style>
</head>
<body>
<form id="workshop">
<label for="wname">Name</label>
<input id="wname" name="name" type="text" value="Arun Kumar">
<fieldset>
<legend>Batch</legend>
<label><input type="radio" name="batch" value="morning" checked> Morning</label>
<label><input type="radio" name="batch" value="evening"> Evening</label>
</fieldset>
<fieldset>
<legend>Topics</legend>
<label><input type="checkbox" name="topics" value="html" checked> HTML</label>
<label><input type="checkbox" name="topics" value="css" checked> CSS</label>
<label><input type="checkbox" name="topics" value="jquery"> jQuery</label>
</fieldset>
<label><input id="agree" type="checkbox"> I agree to the workshop rules</label>
<p><button id="send" type="submit" disabled>Register</button></p>
</form>
<p id="out" role="status">Tick "I agree" to enable the button.</p>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
// Enable the button only when the box is ticked
$('#agree').on('change', (event) => {
$('#send').prop('disabled', !$(event.currentTarget).prop('checked'));
});
$('#workshop').on('submit', (event) => {
event.preventDefault();
const topics = $('input[name="topics"]:checked').map((i, el) => el.value).get();
$('#out').empty(); // clear, then add safe text and elements
$('#out').append(
'serialize(): ', $('<code>').text($('#workshop').serialize()), $('<br>'),
'batch: ', $('<code>').text($('input[name="batch"]:checked').val()), $('<br>'),
'topics array: ', $('<code>').text(JSON.stringify(topics))
);
});
// For this demo: tick the box and submit once, so the result shows
$('#agree').prop('checked', true).trigger('change');
$('#workshop').trigger('submit');
</script>
</body>
</html>Watch out: A disabled submit button is fine for a short form like this, but do not rely on it alone. People cannot tell why a disabled button will not work, and screen readers may skip it. For longer forms, keep the button enabled and explain the problems after submit.
jQuery validation on submit
Listen for submit on the form, call event.preventDefault(), check each field and either show errors or send the data. You do not have to write every rule yourself. HTML attributes such as required, type="email" and pattern already describe the rules, and each input’s validity object tells you which rule failed: valueMissing, typeMismatch, patternMismatch and so on. Adding novalidate to the form turns off the browser’s own pop-up bubbles so that you can show friendlier messages in the page instead.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Inline validation with accessible errors</title>
<style>
body { font-family: system-ui, sans-serif; padding: 16px; line-height: 1.5; }
label { display: block; margin-top: 10px; font-weight: 600; }
input, select { padding: 6px; }
.error { color: #b91c1c; font-size: .9rem; margin: 2px 0 0; }
[aria-invalid="true"] { border: 2px solid #b91c1c; }
#done { color: #166534; }
</style>
</head>
<body>
<form id="enquiry" novalidate>
<label for="name">Full name</label>
<input id="name" name="name" type="text" required value="Meena" aria-describedby="name-error">
<p id="name-error" class="error"></p>
<label for="email">Email</label>
<input id="email" name="email" type="email" required value="meena@" aria-describedby="email-error">
<p id="email-error" class="error"></p>
<label for="mobile">Mobile (10 digits)</label>
<input id="mobile" name="mobile" type="tel" required pattern="[6-9][0-9]{9}" value="98765" aria-describedby="mobile-error">
<p id="mobile-error" class="error"></p>
<label for="city">City</label>
<select id="city" name="city" required aria-describedby="city-error">
<option value="">Choose a city</option>
<option>Puducherry</option>
<option>Chennai</option>
<option>Madurai</option>
</select>
<p id="city-error" class="error"></p>
<p><button type="submit">Send enquiry</button></p>
</form>
<p id="done" role="status"></p>
<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
const messages = {
name: { valueMissing: 'Please enter your name.' },
email: { valueMissing: 'Please enter your email.', typeMismatch: 'Enter an email like [email protected].' },
mobile: { valueMissing: 'Please enter your mobile number.', patternMismatch: 'Enter 10 digits starting with 6, 7, 8 or 9.' },
city: { valueMissing: 'Please choose your city.' }
};
// Returns true if the field is valid, and shows or clears its message
function checkField(field) {
const $field = $(field);
const v = field.validity; // the browser's own checks
const rules = messages[field.name] || {};
let message = '';
if (v.valueMissing) message = rules.valueMissing;
else if (v.typeMismatch) message = rules.typeMismatch;
else if (v.patternMismatch) message = rules.patternMismatch;
else if (field.name === 'name' && $field.val().trim().length < 3) message = 'Name must have at least 3 letters.';
$field.attr('aria-invalid', message ? 'true' : 'false');
$('#' + field.id + '-error').text(message);
return !message;
}
// Check a field when the user leaves it
$('#enquiry').find('input, select').on('blur', (event) => checkField(event.currentTarget));
$('#enquiry').on('submit', (event) => {
event.preventDefault();
const fields = $('#enquiry').find('input, select').get();
const invalid = fields.filter((field) => !checkField(field));
if (invalid.length) {
$('#done').text(`Please fix ${invalid.length} field(s).`);
invalid[0].focus(); // take the user to the first problem
return;
}
$('#done').text('Thanks! We will call you within one working day.');
});
$('#enquiry').trigger('submit'); // demo: show the errors for the sample values
</script>
</body>
</html>The demo starts with wrong sample values and submits once, so you can see the errors. Correct the fields and press “Send enquiry” again.
Making errors accessible
- Put the message in the page, next to the field, in words that say how to fix it – not just “Invalid”.
- Link the message to its field with
aria-describedby, so screen readers read it when the field is focused. - Set
aria-invalid="true"on fields with errors, and back tofalsewhen fixed. - Do not use colour alone; the text message carries the meaning.
- After a failed submit, move focus to the first invalid field.
Run jQuery validation when a field loses focus (blur) rather than on every keystroke. Shouting “Invalid email” after the first letter is annoying; checking when the person moves on feels helpful. The post Web accessibility basics covers more of these rules.
Sending a valid form
Once every field passes, you can send the data with AJAX instead of a full page load. Two details make this feel solid. First, disable the submit button and change its label while the request is travelling, so an impatient double-click does not create two enquiries. Second, re-enable it in .always(), so the person can try again if the network fails. The server’s reply decides the final message: only say “Thanks” after the server confirms.
const $form = $('#enquiry');
const $btn = $form.find('button[type="submit"]');
$btn.prop('disabled', true).text('Sending…');
$.post('/enquiry.php', $form.serialize()) // same data as a normal submit
.done(() => {
$('#done').text('Thanks! We will call you within one working day.');
$form.trigger('reset'); // clear the fields
})
.fail(() => $('#done').text('Sorry, sending failed. Please try again.'))
.always(() => $btn.prop('disabled', false).text('Send enquiry'));You may also find the popular jQuery Validation plugin in older projects. It adds rules and messages through a .validate() method. It is worth recognising, but the approach above uses only the browser’s built-in rules plus a few lines of jQuery, with nothing extra to download.
Tip: Client-side jQuery validation is for convenience only. Anyone can switch off JavaScript or send requests directly, so the server must check everything again. See PHP Forms for the server side.
