Forms and User Input
Reading input values
Forms collect user input through various input types. JavaScript lets you read and validate these values.
Text inputs
<input type="text" id="username" />
const input = document.querySelector('#username');
// Read value
const username = input.value;
console.log(username);
// Update value
input.value = 'new value';
// Listen for changes
input.addEventListener('input', function() {
console.log('Current value:', input.value);
});
Textareas
<textarea id="message"></textarea>
const textarea = document.querySelector('#message');
const message = textarea.value;
Textareas work the same way as text inputs—use .value to read and set their content.
Select dropdowns
<select id="country">
<option value="us">United States</option>
<option value="uk">United Kingdom</option>
<option value="ca">Canada</option>
</select>
const select = document.querySelector('#country');
// Get selected value
const country = select.value; // "us", "uk", or "ca"
// Set selected value
select.value = 'uk';
// Get selected option element
const selectedOption = select.options[select.selectedIndex];
console.log(selectedOption.text); // "United Kingdom"
Checkboxes
<input type="checkbox" id="agree" />
const checkbox = document.querySelector('#agree');
// Check if checked
if (checkbox.checked) {
console.log('Checkbox is checked');
}
// Set checked state
checkbox.checked = true;
// Listen for changes
checkbox.addEventListener('change', function() {
console.log('Checked:', checkbox.checked);
});
Radio buttons
<input type="radio" name="size" value="small" id="size-small" />
<input type="radio" name="size" value="medium" id="size-medium" />
<input type="radio" name="size" value="large" id="size-large" />
// Get selected radio button
const selected = document.querySelector('input[name="size"]:checked');
if (selected) {
console.log('Selected size:', selected.value);
}
// Select a radio button
document.querySelector('#size-medium').checked = true;
// Listen for changes
document.querySelectorAll('input[name="size"]').forEach(radio => {
radio.addEventListener('change', function() {
console.log('Selected:', this.value);
});
});
Preventing default behavior
Forms have a default behavior: they submit to the server and reload the page. In modern web apps, you often want to handle submission with JavaScript instead.
Prevent form submission
<form id="myForm">
<input type="text" name="username" />
<button type="submit">Submit</button>
</form>
const form = document.querySelector('#myForm');
form.addEventListener('submit', function(e) {
e.preventDefault(); // Prevent default form submission
// Get form data
const formData = new FormData(form);
const username = formData.get('username');
// Handle form data yourself
console.log('Username:', username);
// Maybe send to server with fetch, update UI, etc.
});
Why preventDefault() matters
Without preventDefault(), the form submits and the page reloads, losing any JavaScript state. By preventing the default, you keep control and can:
- Validate data before sending
- Show loading states
- Handle errors gracefully
- Update the UI without reloading
Form validation basics
Validation ensures users enter correct data before submission.
HTML5 validation
HTML5 provides built-in validation:
<input type="email" required />
<input type="number" min="0" max="100" />
<input type="text" pattern="[A-Za-z]+" />
const input = document.querySelector('input[type="email"]');
// Check if valid
if (input.validity.valid) {
console.log('Valid email');
} else {
console.log('Invalid email:', input.validationMessage);
}
// Check specific validation errors
if (input.validity.valueMissing) {
console.log('Email is required');
}
if (input.validity.typeMismatch) {
console.log('Not a valid email format');
}
JavaScript validation
For custom validation, check values yourself:
function validateForm(form) {
const username = form.querySelector('#username').value;
const email = form.querySelector('#email').value;
// Check if empty
if (!username.trim()) {
showError('Username is required');
return false;
}
// Check length
if (username.length < 3) {
showError('Username must be at least 3 characters');
return false;
}
// Check email format
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
showError('Invalid email format');
return false;
}
return true;
}
form.addEventListener('submit', function(e) {
e.preventDefault();
if (validateForm(this)) {
// Form is valid, submit it
submitForm(this);
}
});
Showing validation errors
function showError(message) {
// Remove existing errors
const existingError = document.querySelector('.error-message');
if (existingError) {
existingError.remove();
}
// Show new error
const errorDiv = document.createElement('div');
errorDiv.className = 'error-message';
errorDiv.textContent = message;
errorDiv.style.color = 'red';
form.insertBefore(errorDiv, form.firstChild);
}
function clearErrors() {
const errors = document.querySelectorAll('.error-message');
errors.forEach(error => error.remove());
}
Real-time validation
Validate as users type:
const emailInput = document.querySelector('#email');
emailInput.addEventListener('input', function() {
clearErrors();
if (this.value && !isValidEmail(this.value)) {
showFieldError(this, 'Invalid email format');
}
});
function showFieldError(input, message) {
input.style.borderColor = 'red';
let error = input.parentElement.querySelector('.field-error');
if (!error) {
error = document.createElement('span');
error.className = 'field-error';
error.style.color = 'red';
input.parentElement.appendChild(error);
}
error.textContent = message;
}
Working with formdata
The FormData API makes working with forms easier:
const form = document.querySelector('#myForm');
form.addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(form);
// Get specific fields
const username = formData.get('username');
const email = formData.get('email');
// Get all data as object
const data = Object.fromEntries(formData);
// Iterate over all fields
for (const [key, value] of formData.entries()) {
console.log(key, value);
}
// Send to server
fetch('/api/submit', {
method: 'POST',
body: formData // Can send FormData directly
});
Learn more about making HTTP requests with fetch. });
### FormData with file uploads
```html
<input type="file" name="avatar" />
const formData = new FormData(form);
const file = formData.get('avatar');
// file is a File object
console.log(file.name); // filename
console.log(file.size); // file size in bytes
console.log(file.type); // MIME type
// Send file to server
fetch('/api/upload', {
method: 'POST',
body: formData
});
Accessibility considerations
Making forms accessible helps all users, including those using screen readers.
Labels
Always associate labels with inputs:
<!-- Good: explicit association -->
<label for="username">Username</label>
<input type="text" id="username" name="username" />
<!-- Also good: wrapping -->
<label>
Username
<input type="text" name="username" />
</label>
Error messages
Associate error messages with inputs:
<label for="email">Email</label>
<input type="email" id="email" name="email" aria-describedby="email-error" />
<span id="email-error" class="error" role="alert"></span>
function showError(inputId, message) {
const input = document.querySelector(`#${inputId}`);
const error = document.querySelector(`#${inputId}-error`);
input.setAttribute('aria-invalid', 'true');
error.textContent = message;
error.setAttribute('role', 'alert'); // Screen readers announce this
}
Required fields
Indicate required fields clearly:
<label for="username">
Username <span aria-label="required">*</span>
</label>
<input type="text" id="username" required aria-required="true" />
Focus management
Move focus to errors or success messages:
function showError(message) {
const errorDiv = document.createElement('div');
errorDiv.id = 'form-error';
errorDiv.textContent = message;
form.insertBefore(errorDiv, form.firstChild);
// Move focus to error for screen readers
errorDiv.setAttribute('tabindex', '-1');
errorDiv.focus();
}
Common patterns
Form reset
form.reset(); // Resets all form fields to default values
Disable submit button
const submitBtn = form.querySelector('button[type="submit"]');
// Disable while submitting
submitBtn.disabled = true;
submitBtn.textContent = 'Submitting...';
// Re-enable on error
submitBtn.disabled = false;
submitBtn.textContent = 'Submit';
Multi-step forms
let currentStep = 1;
function showStep(step) {
document.querySelectorAll('.form-step').forEach((s, i) => {
s.style.display = i + 1 === step ? 'block' : 'none';
});
}
function nextStep() {
if (validateCurrentStep()) {
currentStep++;
showStep(currentStep);
}
}
function previousStep() {
currentStep--;
showStep(currentStep);
}