📝Forms & Validation
Bygg säkra, användarvänliga formulär med modern validering. Från HTML5-attribut till avancerad JavaScript-validering.
Kodexempel
📝
HTML5 Form Validation
Modern formulärvalidering med HTML5 attribut och JavaScript.
<form id="contactForm" novalidate>
<div className="form-group">
<label for="name">Namn *</label>
<input
type="text"
id="name"
name="name"
required
minlength="2"
pattern="[A-Za-zÅÄÖåäö\s]+"
className="form-control"
>
<span className="error-message"></span>
</div>
<div className="form-group">
<label for="email">E-post *</label>
<input
type="email"
id="email"
name="email"
required
className="form-control"
>
<span className="error-message"></span>
</div>
<div className="form-group">
<label for="phone">Telefon</label>
<input
type="tel"
id="phone"
name="phone"
pattern="[0-9+\s\-()]+"
className="form-control"
>
<span className="error-message"></span>
</div>
<div className="form-group">
<label for="message">Meddelande *</label>
<textarea
id="message"
name="message"
required
minlength="10"
maxlength="500"
rows="4"
className="form-control"
></textarea>
<span className="char-count">0/500</span>
</div>
<button type="submit" className="btn btn-primary">Skicka</button>
</form>
HTML5 validering med required, pattern, minlength och type attribut
✅
JavaScript Validation
Custom validering med JavaScript för avancerad kontroll.
// Form validation class
class FormValidator {
constructor(form) {
this.form = form;
this.errors = {};
this.init();
}
init() {
this.form.addEventListener('submit', (e) => {
e.preventDefault();
if (this.validate()) {
this.submitForm();
}
});
// Real-time validation
this.form.querySelectorAll('input, textarea').forEach(field => {
field.addEventListener('blur', () => this.validateField(field));
field.addEventListener('input', () => this.clearError(field));
});
}
validate() {
this.errors = {};
const fields = this.form.querySelectorAll('[required]');
fields.forEach(field => {
this.validateField(field);
});
return Object.keys(this.errors).length === 0;
}
validateField(field) {
const value = field.value.trim();
const name = field.name;
// Required validation
if (field.required && !value) {
this.setError(field, 'Detta fält är obligatoriskt');
return;
}
// Email validation
if (field.type === 'email' && value) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(value)) {
this.setError(field, 'Ange en giltig e-postadress');
}
}
// Pattern validation
if (field.pattern && value) {
const regex = new RegExp(field.pattern);
if (!regex.test(value)) {
this.setError(field, 'Ogiltigt format');
}
}
// Custom validations
this.customValidations(field);
}
customValidations(field) {
const value = field.value.trim();
// Swedish personal number
if (field.name === 'personnummer' && value) {
if (!this.validatePersonnummer(value)) {
this.setError(field, 'Ogiltigt personnummer');
}
}
// Password strength
if (field.type === 'password' && value) {
const strength = this.checkPasswordStrength(value);
if (strength < 3) {
this.setError(field, 'Lösenordet är för svagt');
}
}
}
setError(field, message) {
this.errors[field.name] = message;
const errorElement = field.parentElement.querySelector('.error-message');
if (errorElement) {
errorElement.textContent = message;
field.classList.add('error');
}
}
clearError(field) {
delete this.errors[field.name];
const errorElement = field.parentElement.querySelector('.error-message');
if (errorElement) {
errorElement.textContent = '';
field.classList.remove('error');
}
}
}
// Initialize validator
const form = document.getElementById('contactForm');
new FormValidator(form);
Omfattande JavaScript-validering med realtidsfeedback och custom regler
📎
File Upload Handling
Säker filuppladdning med validering och förhandsgranskning.
// File upload component
class FileUploader {
constructor(config) {
this.input = document.getElementById(config.inputId);
this.preview = document.getElementById(config.previewId);
this.maxSize = config.maxSize || 5 * 1024 * 1024; // 5MB
this.allowedTypes = config.allowedTypes || ['image/jpeg', 'image/png', 'image/gif'];
this.init();
}
init() {
this.input.addEventListener('change', (e) => this.handleFiles(e.target.files));
// Drag and drop
const dropZone = this.input.closest('.drop-zone');
if (dropZone) {
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
dropZone.classList.add('drag-over');
});
dropZone.addEventListener('dragleave', () => {
dropZone.classList.remove('drag-over');
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
dropZone.classList.remove('drag-over');
this.handleFiles(e.dataTransfer.files);
});
}
}
handleFiles(files) {
Array.from(files).forEach(file => {
if (this.validateFile(file)) {
this.previewFile(file);
this.uploadFile(file);
}
});
}
validateFile(file) {
// Check file type
if (!this.allowedTypes.includes(file.type)) {
alert(`Filtypen ${file.type} är inte tillåten`);
return false;
}
// Check file size
if (file.size > this.maxSize) {
alert(`Filen är för stor. Max storlek: ${this.maxSize / 1024 / 1024}MB`);
return false;
}
return true;
}
previewFile(file) {
if (file.type.startsWith('image/')) {
const reader = new FileReader();
reader.onload = (e) => {
const img = document.createElement('img');
img.src = e.target.result;
img.classList.add('preview-image');
this.preview.appendChild(img);
};
reader.readAsDataURL(file);
}
}
async uploadFile(file) {
const formData = new FormData();
formData.append('file', file);
try {
const response = await fetch('/api/upload', {
method: 'POST',
body: formData
});
if (response.ok) {
const data = await response.json();
console.log('Upload successful:', data);
} else {
throw new Error('Upload failed');
}
} catch (error) {
console.error('Upload error:', error);
}
}
}
// Initialize uploader
new FileUploader({
inputId: 'fileInput',
previewId: 'filePreview',
maxSize: 10 * 1024 * 1024, // 10MB
allowedTypes: ['image/jpeg', 'image/png', 'application/pdf']
});
Komplett filuppladdning med drag & drop, validering och förhandsgranskning
🔒
Form Security
Säkerhetsåtgärder för formulär mot vanliga attacker.
// CSRF Token handling
class SecureForm {
constructor(formId) {
this.form = document.getElementById(formId);
this.csrfToken = this.getCSRFToken();
this.init();
}
init() {
// Add CSRF token to form
const tokenInput = document.createElement('input');
tokenInput.type = 'hidden';
tokenInput.name = '_csrf';
tokenInput.value = this.csrfToken;
this.form.appendChild(tokenInput);
// Sanitize inputs
this.form.addEventListener('submit', (e) => {
e.preventDefault();
this.sanitizeInputs();
this.submitSecurely();
});
// Rate limiting
this.setupRateLimit();
}
getCSRFToken() {
// Get from meta tag or cookie
const meta = document.querySelector('meta[name="csrf-token"]');
return meta ? meta.content : this.getCookie('XSRF-TOKEN');
}
sanitizeInputs() {
const inputs = this.form.querySelectorAll('input[type="text"], textarea');
inputs.forEach(input => {
// Basic XSS prevention
input.value = this.escapeHtml(input.value);
});
}
escapeHtml(text) {
const map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
return text.replace(/[&<>"']/g, m => map[m]);
}
setupRateLimit() {
let submitCount = 0;
const maxSubmits = 3;
const resetTime = 60000; // 1 minute
this.form.addEventListener('submit', (e) => {
submitCount++;
if (submitCount > maxSubmits) {
e.preventDefault();
alert('För många försök. Vänta en minut.');
setTimeout(() => { submitCount = 0; }, resetTime);
}
});
}
async submitSecurely() {
const formData = new FormData(this.form);
try {
const response = await fetch(this.form.action, {
method: 'POST',
headers: {
'X-CSRF-Token': this.csrfToken
},
body: formData,
credentials: 'same-origin'
});
if (response.ok) {
// Handle success
window.location.href = '/success';
} else {
// Handle errors
const error = await response.json();
this.displayError(error.message);
}
} catch (error) {
console.error('Submission error:', error);
}
}
}
// Initialize secure form
new SecureForm('secureContactForm');
Säkerhetsimplementering med CSRF-skydd, input-sanering och rate limiting
Best Practices
👤
Användarvänlighet
- •Tydliga labels och instruktioner
- •Logisk tabb-ordning
- •Inline validering med direkt feedback
- •Spara formulärdata lokalt vid sidladdning
🔐
Säkerhet
- •Validera alltid på serversidan
- •Använd HTTPS för alla formulär
- •Implementera CSRF-skydd
- •Sanera all användarinput
♿
Tillgänglighet
- •Använd semantiska HTML-element
- •Koppla labels till inputs med 'for'
- •Ge tydliga felmeddelanden
- •Stöd tangentbordsnavigering
⚡
Performance
- •Debounce realtidsvalidering
- •Lazy load stora formulär
- •Optimera filuppladdningar
- •Använd Web Workers för tung validering
🎯 HTML5 Input Types
Text Inputs
type="text"
- Vanlig texttype="email"
- E-postadresstype="tel"
- Telefonnummertype="url"
- Webbadresstype="search"
- Sökfält
Date & Time
type="date"
- Datumtype="time"
- Tidtype="datetime-local"
- Datum & tidtype="month"
- Månadtype="week"
- Vecka
Numeric & Other
type="number"
- Nummertype="range"
- Slidertype="color"
- Färgväljaretype="file"
- Filuppladdningtype="hidden"
- Dolt fält
🔍 Vanliga valideringsmönster
Svenska format
- Personnummer:
[0-9]{6}-?[0-9]{4}
- Postnummer:
[0-9]{3}\s?[0-9]{2}
- Telefon:
0[0-9]{1,3}-?[0-9]{5,8}
- Bankgiro:
[0-9]{3,4}-[0-9]{4}
Allmänna format
- E-post:
[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}
- URL:
https?://[\w\-._~:/?#[\]@!$&'()*+,;=.]+
- Lösenord (stark):
(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}
- Hex-färg:
#[0-9A-Fa-f]{6}