दिन 13: Forms, FormData और Validation
✅ आज आप सीखेंगे
- preventDefault के साथ submit संभालना
- FormData और Object.fromEntries से सब fields पढ़ना
- साफ़ validation नियम (mobile, PIN, quantity, date)
- Fields के पास accessible error messages
- Constraint Validation API और live गणना
🏢 असली दुनिया में
HTML course का सिर्फ़-HTML order form साफ़ ग़लत data रोकता था, पर customers अब भी कल की date के लिए order करते, delivery area के बाहर के PIN लिखते और सिर्फ़ English में अस्पष्ट browser bubble पाते थे। ख़राब forms पर businesses orders खोते हैं। आज आप ऐसा दोस्ताना form बनाएँगे जो हर ग़लती समझाए और total live जोड़े।
Submit संभालना और values पढ़ना
<style>
form { display: grid; gap: 10px; max-width: 380px; }
label { font-weight: 600; display: block; margin-bottom: 2px; }
input, select { width: 100%; box-sizing: border-box; }
.error { color: #b91c1c; font-size: .85rem; min-height: 1.1em; }
input[aria-invalid="true"], select[aria-invalid="true"] { border-color: #dc2626; background: #fef2f2; }
.ok { background: #dcfce7; padding: 10px; border-radius: 8px; }
</style>
<form id="f">
<label for="name">Name</label><input id="name" name="name" value="Aarti Gupta">
<label for="sweet">Sweet</label>
<select id="sweet" name="sweet"><option value="1">Kaju Katli</option><option value="2" selected>Ghewar</option></select>
<label><input type="checkbox" name="gift" checked> Gift wrap</label>
<fieldset><legend>Delivery</legend>
<label><input type="radio" name="mode" value="home" checked> Home</label>
<label><input type="radio" name="mode" value="pickup"> Pick-up</label>
</fieldset>
<button>Submit</button>
</form>
<pre id="out"></pre>
<script>
document.querySelector('#f').addEventListener('submit', function (e) {
e.preventDefault(); // no page reload
const data = new FormData(e.currentTarget); // reads every named field
const order = Object.fromEntries(data); // → plain object
order.gift = data.has('gift'); // unchecked boxes are missing
order.sweet = Number(order.sweet);
document.querySelector('#out').textContent = JSON.stringify(order, null, 2);
});
</script>FormData सिर्फ़ name वाले fields पढ़ता है। Radio buttons एक name साझा करते हैं और चुनी value देते हैं; बिना tick वाला checkbox शामिल ही नहीं होता – data.has("gift") से जाँचें।
छोटे functions के रूप में validation नियम
const DELIVERY_PINS = ['302001', '302003', '302004', '302012', '302017'];
const rules = {
name: v => v.trim().length >= 2 || 'Please enter your full name.',
phone: v => /^[6-9]\d{9}$/.test(v.replace(/\D/g, '').slice(-10)) || 'Enter a 10-digit mobile number starting with 6–9.',
pin: v => DELIVERY_PINS.includes(v.trim()) || 'Sorry, we do not deliver to this PIN yet.',
qty: v => (Number(v) >= 0.25 && Number(v) <= 50) || 'Quantity must be between 0.25 and 50 kg.',
date: (v, today) => v >= today || 'Choose today or a future date.',
};
function validate(order, today) {
const errors = {};
for (const [field, rule] of Object.entries(rules)) {
const result = rule(order[field] ?? '', today);
if (result !== true) errors[field] = result;
}
return errors;
}
const today = '2026-10-25';
console.log(validate({ name: 'Aarti', phone: '+91 98290 12345', pin: '302003', qty: '2', date: '2026-10-26' }, today));
console.log(validate({ name: 'A', phone: '12345', pin: '110001', qty: '0', date: '2026-10-20' }, today));हर नियम “ठीक है?” का जवाब true से देता है, या समझाता है क्या ग़लत है। वही object server (Node.js) पर दोबारा इस्तेमाल हो सकता है – और logic छुए बिना messages Hindi में बदल सकते हैं।
पूरा validated order form
<style>
form { display: grid; gap: 10px; max-width: 380px; }
label { font-weight: 600; display: block; margin-bottom: 2px; }
input, select { width: 100%; box-sizing: border-box; }
.error { color: #b91c1c; font-size: .85rem; min-height: 1.1em; }
input[aria-invalid="true"], select[aria-invalid="true"] { border-color: #dc2626; background: #fef2f2; }
.ok { background: #dcfce7; padding: 10px; border-radius: 8px; }
</style>
<form id="order" novalidate>
<div><label for="name">Name</label><input id="name" name="name" autocomplete="name" aria-describedby="name-err"><div class="error" id="name-err"></div></div>
<div><label for="phone">Mobile</label><input id="phone" name="phone" type="tel" inputmode="numeric" autocomplete="tel" aria-describedby="phone-err"><div class="error" id="phone-err"></div></div>
<div><label for="pin">PIN code</label><input id="pin" name="pin" inputmode="numeric" maxlength="6" aria-describedby="pin-err"><div class="error" id="pin-err"></div></div>
<div><label for="qty">Kaju Katli (kg) – ₹900/kg</label><input id="qty" name="qty" type="number" value="1" step="0.25" aria-describedby="qty-err"><div class="error" id="qty-err"></div></div>
<div><label for="date">Delivery date</label><input id="date" name="date" type="date" aria-describedby="date-err"><div class="error" id="date-err"></div></div>
<p id="total"></p>
<button>Place order</button>
<div id="result" role="status"></div>
</form>
<script>
const PINS = ['302001', '302003', '302004', '302012', '302017'];
const today = new Date().toISOString().slice(0, 10);
const form = document.querySelector('#order');
form.date.min = today;
const rules = {
name: v => v.trim().length >= 2 || 'Please enter your full name.',
phone: v => /^[6-9]\d{9}$/.test(v.replace(/\D/g, '').slice(-10)) || 'Enter a 10-digit mobile number starting with 6–9.',
pin: v => PINS.includes(v.trim()) || 'We deliver to 302001, 302003, 302004, 302012, 302017.',
qty: v => (Number(v) >= 0.25 && Number(v) <= 50) || 'Between 0.25 and 50 kg.',
date: v => (v !== '' && v >= today) || 'Choose today or a future date.',
};
function showError(field, message) {
const input = form.elements[field];
input.setAttribute('aria-invalid', message ? 'true' : 'false');
document.querySelector('#' + field + '-err').textContent = message || '';
}
function check(field) {
const result = rules[field](form.elements[field].value);
showError(field, result === true ? '' : result);
return result === true;
}
function updateTotal() {
const kg = Number(form.qty.value) || 0;
const amount = kg * 900;
const delivery = amount >= 1500 || amount === 0 ? 0 : 60;
document.querySelector('#total').textContent = `Total ₹${amount + delivery} (delivery ₹${delivery})`;
}
Object.keys(rules).forEach(f => form.elements[f].addEventListener('blur', () => check(f))); // validate on leave
form.addEventListener('input', e => {
if (e.target.getAttribute('aria-invalid') === 'true') check(e.target.name); // re-check while fixing
if (e.target.name === 'qty') updateTotal();
});
form.addEventListener('submit', e => {
e.preventDefault();
const results = Object.keys(rules).map(check);
const firstBad = Object.keys(rules).find((f, i) => !results[i]);
if (firstBad) { form.elements[firstBad].focus(); return; } // move focus to the first error
const data = Object.fromEntries(new FormData(form));
document.querySelector('#result').innerHTML = `<p class="ok">✅ Order for ${data.qty} kg booked for ${data.date}. We will call ${data.phone}.</p>`;
});
updateTotal();
</script>| UX नियम | हमने कैसे किया |
|---|---|
| User के लिखने से पहले न चिल्लाएँ | Page load पर नहीं, blur और submit पर validate |
| ठीक होते ही error हटे | Input पर सिर्फ़ invalid fields दोबारा जाँचें |
| ठीक-ठीक बताएँ क्या करना है | “6–9 से शुरू 10-digit mobile लिखें” |
| Accessible errors | aria-invalid + aria-describedby message को field से जोड़ते हैं |
| समस्या तक पहुँचाएँ | Submit पर पहले invalid field पर focus |
| इंसानी formats मानें | +91 98290-12345 → जाँच से पहले अंक निकाले |
कोई भी JavaScript bypass कर सकता है (DevTools, curl)। Server को सब कुछ दोबारा validate करना होगा – Day 20 का हमारा practice API यही करता है और HTTP 422 के साथ field errors लौटाता है।
Built-in Constraint Validation API
<style>
form { display: grid; gap: 10px; max-width: 380px; }
label { font-weight: 600; display: block; margin-bottom: 2px; }
input, select { width: 100%; box-sizing: border-box; }
.error { color: #b91c1c; font-size: .85rem; min-height: 1.1em; }
input[aria-invalid="true"], select[aria-invalid="true"] { border-color: #dc2626; background: #fef2f2; }
.ok { background: #dcfce7; padding: 10px; border-radius: 8px; }
</style>
<form id="f2">
<label for="email">E-mail</label>
<input id="email" type="email" required>
<button>Subscribe</button>
</form>
<script>
const email = document.querySelector('#email');
email.addEventListener('input', () => {
if (email.validity.typeMismatch) email.setCustomValidity('Please enter an e-mail like name@example.com');
else if (email.validity.valueMissing) email.setCustomValidity('E-mail is required to get offers');
else email.setCustomValidity(''); // '' = valid again
});
document.querySelector('#f2').addEventListener('submit', e => {
e.preventDefault();
if (!e.currentTarget.checkValidity()) { e.currentTarget.reportValidity(); return; }
e.currentTarget.insertAdjacentHTML('beforeend', '<p class="ok">Subscribed ✅</p>');
});
</script>🏅 आज के Job Skills
Practice tasks
🏋️ Practice – ख़ुद करें
Solution देखें
Quick quiz
🧠 Quick Quiz – ख़ुद को जाँचें
Interview corner
💼 Interview Corner
Vanilla JS में forms अच्छे से कैसे validate करें?
Basics के लिए HTML attributes, नियम functions में एक जगह, blur/submit पर validate, inline accessible messages (aria-invalid, aria-describedby), पहली error पर focus, input साफ़ (trim, digits) और server पर हमेशा दोबारा validate।
Submit पर e.preventDefault() क्या करता है?
Browser का default navigation/reload रोकता है ताकि JavaScript page पर रहते हुए data validate करके (जैसे fetch से) भेज सके।
सारांश
- submit + preventDefault + FormData + Object.fromEntries।
- नियम छोटे functions जो true या message लौटाएँ।
- blur/submit पर validate, accessible inline errors, पहली पर focus।
- Client जाँच UX है; server को दोबारा validate करना होगा।
Day 14 – Mini project: add, edit, complete, filter, delete और saving वाला पूरा to-do app।
