Lesson 17: HTML Forms Part 3
Master file uploads, hidden fields, form security, and advanced form styling techniques
Start LearningFile Uploads
The <input type="file">
element allows users to select one or more files from their device storage to upload to a server.
enctype
attribute to multipart/form-data
and the method to POST
.
Basic File Upload
<form action="/upload" method="post" enctype="multipart/form-data"> <div class="form-group"> <label for="avatar">Upload Profile Picture:</label> <input type="file" id="avatar" name="avatar" accept="image/*"> </div> <button type="submit">Upload</button> </form>
Result:
Multiple File Upload
Add the multiple
attribute to allow selecting multiple files:
<form action="/upload" method="post" enctype="multipart/form-data"> <div class="form-group"> <label for="photos">Upload Photos:</label> <input type="file" id="photos" name="photos" accept="image/*" multiple> </div> <button type="submit">Upload Files</button> </form>
Result:
File Upload with Drag & Drop
Note: Drag and drop functionality requires JavaScript to handle the events.
<div id="drop-area" class="file-upload-container"> <h4>Drag & Drop Files Here</h4> <p>or</p> <input type="file" id="file-input" multiple style="display:none;"> <button class="file-upload-btn" onclick="document.getElementById('file-input').click()"> Select Files </button> <div id="file-list" class="file-list"></div> </div> <script> const dropArea = document.getElementById('drop-area'); const fileInput = document.getElementById('file-input'); const fileList = document.getElementById('file-list'); // Prevent default drag behaviors ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { dropArea.addEventListener(eventName, preventDefaults, false); }); function preventDefaults(e) { e.preventDefault(); e.stopPropagation(); } // Highlight drop area when item is dragged over it ['dragenter', 'dragover'].forEach(eventName => { dropArea.addEventListener(eventName, highlight, false); }); ['dragleave', 'drop'].forEach(eventName => { dropArea.addEventListener(eventName, unhighlight, false); }); function highlight() { dropArea.classList.add('drag-over'); } function unhighlight() { dropArea.classList.remove('drag-over'); } // Handle dropped files dropArea.addEventListener('drop', handleDrop, false); function handleDrop(e) { const dt = e.dataTransfer; const files = dt.files; handleFiles(files); } // Handle selected files from input fileInput.addEventListener('change', function() { handleFiles(this.files); }); function handleFiles(files) { for (let i = 0; i < files.length; i++) { const file = files[i]; displayFile(file); } } function displayFile(file) { const fileItem = document.createElement('div'); fileItem.className = 'file-item'; const fileInfo = document.createElement('div'); fileInfo.style.display = 'flex'; fileInfo.style.alignItems = 'center'; fileInfo.style.flex = '1'; const fileIcon = document.createElement('i'); fileIcon.className = 'fas fa-file file-icon'; const fileName = document.createElement('div'); fileName.className = 'file-name'; fileName.textContent = file.name; const fileSize = document.createElement('div'); fileSize.className = 'file-size'; fileSize.textContent = formatFileSize(file.size); const removeBtn = document.createElement('button'); removeBtn.className = 'remove-file'; removeBtn.innerHTML = ''; removeBtn.onclick = function() { fileItem.remove(); }; fileInfo.appendChild(fileIcon); fileInfo.appendChild(fileName); fileInfo.appendChild(fileSize); fileItem.appendChild(fileInfo); fileItem.appendChild(removeBtn); fileList.appendChild(fileItem); } function formatFileSize(bytes) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } </script>
Result:
Drag & Drop Files Here
or
Hidden Fields
Hidden fields allow you to include data that isn't visible to the user but is submitted with the form.
Basic Hidden Field
<form action="/submit" method="post"> <!-- Visible fields --> <div class="form-group"> <label for="name">Name:</label> <input type="text" id="name" name="name" required> </div> <!-- Hidden field --> <input type="hidden" id="user-id" name="user_id" value="12345"> <button type="submit">Submit</button> </form>
Result:
Hidden fields are not visible to the user but are included in form submission.
Security: CSRF Tokens
Security Alert: Always include CSRF tokens in your forms to prevent Cross-Site Request Forgery attacks.
<form action="/update-profile" method="post"> <div class="form-group"> <label for="email">Email:</label> <input type="email" id="email" name="email" required> </div> <!-- CSRF Token --> <input type="hidden" name="csrf_token" value="a1b2c3d4e5f6g7h8i9j0"> <button type="submit">Update Email</button> </form>
Result:
CSRF tokens protect against malicious form submissions.
Advanced Form Styling
Create visually appealing forms with custom styling for form controls using CSS.
Custom Checkboxes
<style> .custom-checkbox { display: flex; align-items: center; margin-bottom: 10px; cursor: pointer; } .custom-checkbox input { position: absolute; opacity: 0; cursor: pointer; height: 0; width: 0; } .checkmark { height: 20px; width: 20px; background-color: #eee; border: 1px solid #ccc; border-radius: 4px; margin-right: 10px; position: relative; transition: all 0.2s; } .custom-checkbox:hover input ~ .checkmark { background-color: #ddd; } .custom-checkbox input:checked ~ .checkmark { background-color: var(--primary); border-color: var(--primary-dark); } .checkmark:after { content: ""; position: absolute; display: none; left: 6px; top: 2px; width: 5px; height: 10px; border: solid white; border-width: 0 3px 3px 0; transform: rotate(45deg); } .custom-checkbox input:checked ~ .checkmark:after { display: block; } </style> <div class="form-group"> <label>Notification Preferences:</label> <label class="custom-checkbox"> <input type="checkbox" name="email_notifications" checked> <span class="checkmark"></span> Email Notifications </label> <label class="custom-checkbox"> <input type="checkbox" name="push_notifications"> <span class="checkmark"></span> Push Notifications </label> <label class="custom-checkbox"> <input type="checkbox" name="sms_notifications"> <span class="checkmark"></span> SMS Notifications </label> </div>
Result:
Custom Radio Buttons
<style> .custom-radio { display: flex; align-items: center; margin-bottom: 10px; cursor: pointer; } .custom-radio input { position: absolute; opacity: 0; cursor: pointer; height: 0; width: 0; } .radiomark { height: 20px; width: 20px; background-color: #eee; border: 1px solid #ccc; border-radius: 50%; margin-right: 10px; position: relative; transition: all 0.2s; } .custom-radio:hover input ~ .radiomark { background-color: #ddd; } .custom-radio input:checked ~ .radiomark { background-color: var(--primary); border-color: var(--primary-dark); } .radiomark:after { content: ""; position: absolute; display: none; top: 5px; left: 5px; width: 8px; height: 8px; border-radius: 50%; background: white; } .custom-radio input:checked ~ .radiomark:after { display: block; } </style> <div class="form-group"> <label>Preferred Contact Method:</label> <label class="custom-radio"> <input type="radio" name="contact" value="email" checked> <span class="radiomark"></span> Email </label> <label class="custom-radio"> <input type="radio" name="contact" value="phone"> <span class="radiomark"></span> Phone </label> <label class="custom-radio"> <input type="radio" name="contact" value="mail"> <span class="radiomark"></span> Postal Mail </label> </div>
Result:
Custom Select Dropdown
<style> .custom-select { position: relative; margin-bottom: 15px; } .custom-select select { appearance: none; background-color: white; padding: 10px 40px 10px 15px; width: 100%; border: 1px solid #ddd; border-radius: 4px; font-size: 1rem; cursor: pointer; } .custom-select::after { content: ""; position: absolute; top: 50%; right: 15px; width: 0; height: 0; border-left: 6px solid transparent; border-right: 6px solid transparent; border-top: 6px solid var(--primary); transform: translateY(-50%); pointer-events: none; } </style> <div class="form-group"> <label for="country">Country:</label> <div class="custom-select"> <select id="country" name="country"> <option value="">Select a country</option> <option value="us">United States</option> <option value="ca">Canada</option> <option value="uk">United Kingdom</option> <option value="au">Australia</option> <option value="de">Germany</option> <option value="fr">France</option> <option value="jp">Japan</option> </select> </div> </div>
Result:
Working with Form Data
JavaScript provides the FormData API for easily working with form data, especially when submitting forms via AJAX.
FormData API Example
<form id="user-form"> <div class="form-group"> <label for="username">Username:</label> <input type="text" id="username" name="username" required> </div> <div class="form-group"> <label for="user-email">Email:</label> <input type="email" id="user-email" name="email" required> </div> <div class="form-group"> <label for="user-avatar">Avatar:</label> <input type="file" id="user-avatar" name="avatar" accept="image/*"> </div> <div class="form-actions"> <button type="submit">Submit</button> </div> </form> <div id="form-data-output" style="margin-top: 20px; padding: 15px; background: #f9f9f9; border-radius: 8px;"> <h4>Form Data Output:</h4> <pre id="output"></pre> </div> <script> document.getElementById('user-form').addEventListener('submit', function(e) { e.preventDefault(); const form = e.target; const formData = new FormData(form); // Display form data let output = ''; for (let [key, value] of formData.entries()) { output += `${key}: ${value}\n`; } document.getElementById('output').textContent = output; // In a real application, you would send the data to a server // Example using fetch: /* fetch('/submit', { method: 'POST', body: formData }) .then(response => response.json()) .then(data => { console.log('Success:', data); }) .catch(error => { console.error('Error:', error); }); */ }); </script>
Result:
Form Data Output:
Practical Exercise: File Upload Form
Create a file upload form with the following features:
Your Task:
Create a document upload form with:
- Personal information fields (name, email)
- Document type selection (dropdown)
- File upload with drag & drop support
- Custom styled checkboxes for consent
- CSRF token for security
- Form validation
- AJAX submission using FormData