Form Events
Forms have specific events for handling user input.
Submit Event
const form = document.querySelector('form');
form.addEventListener('submit', (e) => {
e.preventDefault(); // Prevent page reload
// Get form data
const formData = new FormData(form);
const data = Object.fromEntries(formData);
console.log(data); // { name: 'Alice', email: 'alice@example.com' }
// Send to server
fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
});
Input Event
const input = document.querySelector('input');
// Fires on every value change
input.addEventListener('input', (e) => {
console.log('Current value:', e.target.value);
console.log('Selection start:', e.target.selectionStart);
});
// Fires when value changes and element loses focus
input.addEventListener('change', (e) => {
console.log('Final value:', e.target.value);
});
Focus and Blur
const input = document.querySelector('input');
// When element receives focus
input.addEventListener('focus', () => {
input.classList.add('focused');
});
// When element loses focus
input.addEventListener('blur', () => {
input.classList.remove('focused');
// Validate on blur
validateInput(input);
});
Other Form Events
// Reset event
form.addEventListener('reset', () => {
console.log('Form reset');
clearErrors();
});
// Invalid event
input.addEventListener('invalid', (e) => {
e.preventDefault(); // Prevent browser validation UI
showError(input, 'Invalid value');
});
// Select event (text inputs)
input.addEventListener('select', () => {
console.log('Text selected:', input.value.substring(
input.selectionStart,
input.selectionEnd
));
});
Real-time Validation
const emailInput = document.querySelector('input[type="email"]');
emailInput.addEventListener('input', (e) => {
const email = e.target.value;
const isValid = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email);
emailInput.classList.toggle('valid', isValid);
emailInput.classList.toggle('invalid', !isValid && email.length > 0);
const errorEl = emailInput.nextElementSibling;
if (!isValid && email.length > 0) {
errorEl.textContent = 'Please enter a valid email';
errorEl.style.display = 'block';
} else {
errorEl.style.display = 'none';
}
});
FormData API
FormData provides an easy way to collect and send form data.
Creating FormData
// From form element
const form = document.querySelector('form');
const formData = new FormData(form);
// Manually
const formData = new FormData();
formData.append('name', 'Alice');
formData.append('email', 'alice@example.com');
// From another FormData
const copy = new FormData(formData);
FormData Methods
const formData = new FormData();
// Add data
formData.append('name', 'Alice');
formData.append('hobbies', 'reading');
formData.append('hobbies', 'gaming'); // Multiple values
// Set (replaces existing)
formData.set('name', 'Bob');
// Get values
console.log(formData.get('name')); // 'Bob'
console.log(formData.getAll('hobbies')); // ['reading', 'gaming']
// Check existence
console.log(formData.has('email')); // false
// Delete
formData.delete('hobbies');
// Iterate
for (const [key, value] of formData) {
console.log(`${key}: ${value}`);
}
Sending FormData
const form = document.querySelector('form');
form.addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(form);
// Method 1: Fetch with FormData (auto sets Content-Type)
const response = await fetch('/api/users', {
method: 'POST',
body: FormData // Browser sets Content-Type: multipart/form-data
});
// Method 2: Convert to JSON
const data = Object.fromEntries(formData);
const response2 = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
// Method 3: URL encoded
const encoded = new URLSearchParams(formData);
const response3 = await fetch('/api/users', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: encoded
});
});
File Uploads
const form = document.querySelector('form');
form.addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(form); // Automatically includes files
// Add additional data
formData.append('userId', '123');
const response = await fetch('/api/upload', {
method: 'POST',
body: FormData // multipart/form-data
});
});
// Access files
const fileInput = document.querySelector('input[type="file"]');
fileInput.addEventListener('change', (e) => {
const files = e.target.files;
console.log('Selected files:', files.length);
for (const file of files) {
console.log(file.name, file.size, file.type);
}
});
Validation Patterns
Form validation ensures data integrity before submission.
HTML5 Validation
<form>
<input type="email" required>
<input type="text" minlength="3" maxlength="50">
<input type="number" min="0" max="100">
<input type="text" pattern="[A-Za-z]{3}">
<button type="submit">Submit</button>
</form>
JavaScript Validation
const form = document.querySelector('form');
form.addEventListener('submit', (e) => {
const errors = validateForm(form);
if (Object.keys(errors).length > 0) {
e.preventDefault();
showErrors(errors);
return;
}
// Submit form
});
function validateForm(form) {
const errors = {};
const name = form.querySelector('[name="name"]');
if (name.value.length < 2) {
errors.name = 'Name must be at least 2 characters';
}
const email = form.querySelector('[name="email"]');
if (!/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(email.value)) {
errors.email = 'Please enter a valid email';
}
const age = form.querySelector('[name="age"]');
if (age.value < 18 || age.value > 120) {
errors.age = 'Age must be between 18 and 120';
}
return errors;
}
function showErrors(errors) {
// Clear previous errors
document.querySelectorAll('.error').forEach(el => {
el.classList.remove('error');
el.nextElementSibling?.remove();
});
// Show new errors
Object.entries(errors).forEach(([field, message]) => {
const input = document.querySelector(`[name="${field}"]`);
input.classList.add('error');
const errorEl = document.createElement('span');
errorEl.className = 'error-message';
errorEl.textContent = message;
input.after(errorEl);
});
}
Real-time Validation
function setupValidation(form) {
const inputs = form.querySelectorAll('input, select, textarea');
inputs.forEach(input => {
input.addEventListener('blur', () => {
validateField(input);
});
input.addEventListener('input', () => {
if (input.classList.contains('error')) {
validateField(input);
}
});
});
}
function validateField(input) {
const rules = {
required: (v) => v.length > 0 || 'This field is required',
email: (v) => /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(v) || 'Invalid email',
minLength: (min) => (v) =>
v.length >= min || `Must be at least ${min} characters`
};
const validators = input.dataset;
let error = null;
if (validators.required && !rules.required(input.value)) {
error = rules.required(input.value);
} else if (validators.email && !rules.email(input.value)) {
error = rules.email(input.value);
} else if (validators.minLength) {
error = rules.minLength(parseInt(validators.minLength))(input.value);
}
if (error) {
input.classList.add('error');
input.classList.remove('valid');
showError(input, error);
} else {
input.classList.remove('error');
input.classList.add('valid');
hideError(input);
}
return !error;
}
Practice Problems
Create a reusable React component implementing Form Handling. Include proper state management and accessibility.
Solution
// Production-ready component with:
// - Proper TypeScript types
// - Accessibility (ARIA)
// - Error boundaries
// - Loading states
// - Memoization where neededWrite unit and integration tests for Form Handling using React Testing Library.
Solution
// Test coverage:
// 1. Rendering tests
// 2. Interaction tests
// 3. Edge case tests
// 4. Accessibility testsOptimize Form Handling for performance. Consider memoization, code splitting, and bundle size.
Solution
// Optimization techniques:
// 1. React.memo / useMemo / useCallback
// 2. Code splitting with lazy()
// 3. Virtual scrolling for lists
// 4. Image lazy loading
// 5. Bundle analysisQuiz
1. How do you prevent a form from reloading the page?
2. What's the difference between 'input' and 'change' events?
3. How do you get form data as an object?
4. What Content-Type does FormData use when sent with fetch?
Flashcards
Question
How to prevent form submission?
Click to reveal answer
Answer
e.preventDefault() in the submit event handler prevents the default page reload.
Question
What is FormData?
Click to reveal answer
Answer
An API that collects form data as key-value pairs, supporting text and file inputs.
Question
input vs change event?
Click to reveal answer
Answer
input fires on every value change. change fires when value changes and element loses focus.
Question
How to send form data as JSON?
Click to reveal answer
Answer
Convert FormData to object: Object.fromEntries(new FormData(form)), then JSON.stringify().
Question
What is Form Handling?
Click to reveal answer
Answer
Form Handling is a key concept in frontend development.
Revision Notes
Key Takeaways
- 1.Always use preventDefault() on form submit
- 2.FormData provides easy form data access
- 3.input fires immediately, change fires on blur
- 4.Validate on both submit and blur for good UX
- 5.Use FormData for file uploads
Interview Tips
- •Show form submission handling with preventDefault()
- •Demonstrate FormData usage for data collection
- •Implement real-time validation with input and blur events
- •Explain how to handle file uploads with FormData
Cheat Sheet
Form Handling Cheat Sheet
Form Events
submit: Form submission (use preventDefault)input: Every value changechange: Value change on blurfocus/blur: Focus state
FormData API
const data = new FormData(form);
data.get('name');
data.getAll('hobbies');
Object.fromEntries(data); // to object
Validation
- Use HTML5 attributes (required, pattern, etc.)
- Validate on submit and blur
- Show errors inline
Sending Data
- FormData: multipart/form-data
- Object.fromEntries: JSON
- URLSearchParams: URL encoded