Форма з валідацією та localStorage
Створи повноцінну форму реєстрації з валідацією в реальному часі та збереженням даних у localStorage. Це типове завдання, яке зустрічається на будь-якому вебсайті.
Файлова структура
form-validation/
├── index.html
├── style.css
└── script.js
Частина 1: HTML-форма
Створи форму реєстрації з наступними полями:
<form id="registration-form" novalidate>
<div class="form-group">
<label for="name">Ім'я</label>
<input type="text" id="name" name="name" placeholder="Мінімум 2 символи">
<span class="error-message"></span>
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email" placeholder="example@mail.com">
<span class="error-message"></span>
</div>
<div class="form-group">
<label for="password">Пароль</label>
<input type="password" id="password" name="password" placeholder="Мінімум 8 символів, включно з цифрою">
<span class="error-message"></span>
</div>
<div class="form-group">
<label for="confirm-password">Підтвердження пароля</label>
<input type="password" id="confirm-password" name="confirmPassword" placeholder="Повторіть пароль">
<span class="error-message"></span>
</div>
<div class="form-group">
<label for="age">Вік</label>
<input type="number" id="age" name="age" placeholder="16-100">
<span class="error-message"></span>
</div>
<div class="form-group">
<label>
<input type="checkbox" id="terms" name="terms">
Я погоджуюсь з умовами використання
</label>
<span class="error-message"></span>
</div>
<button type="submit">Зареєструватися</button>
</form>
Атрибут novalidate вимикає вбудовану браузерну валідацію. Ми напишемо свою.
Частина 2: Правила валідації
Створи функції валідації для кожного поля.
Правила
| Поле | Правило | Повідомлення при помилці |
|---|---|---|
| Ім'я | Мінімум 2 символи | "Ім'я має містити мінімум 2 символи" |
| Відповідає патерну email | "Введіть коректний email" | |
| Пароль | Мінімум 8 символів + хоча б 1 цифра | "Пароль: мінімум 8 символів та 1 цифра" |
| Підтвердження | Збігається з паролем | "Паролі не збігаються" |
| Вік | Число від 16 до 100 | "Вік має бути від 16 до 100" |
| Умови | Чекбокс обов'язково відмічений | "Необхідно погодитись з умовами" |
Приклад реалізації
// Функція валідації одного поля
function validateField(field, value) {
switch (field) {
case 'name':
if (value.trim().length < 2) {
return 'Ім\'я має містити мінімум 2 символи';
}
return '';
case 'email':
// Простий regex для email
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(value)) {
return 'Введіть коректний email';
}
return '';
case 'password':
if (value.length < 8) {
return 'Пароль має містити мінімум 8 символів';
}
if (!/\d/.test(value)) {
return 'Пароль має містити хоча б 1 цифру';
}
return '';
// ... інші поля
}
}
Частина 3: Валідація в реальному часі
Валідація має працювати на подіях input та blur.
Вимоги
1. Подія blur (коли поле втрачає фокус):
- Перевірити поле та показати помилку, якщо значення некоректне
2. Подія input (під час набору):
- Якщо поле вже має помилку, перевіряти під час набору та прибирати помилку, коли значення стане коректним
// Показати помилку
function showError(input, message) {
const formGroup = input.closest('.form-group');
const errorSpan = formGroup.querySelector('.error-message');
formGroup.classList.add('error');
formGroup.classList.remove('success');
errorSpan.textContent = message;
}
// Показати успіх
function showSuccess(input) {
const formGroup = input.closest('.form-group');
const errorSpan = formGroup.querySelector('.error-message');
formGroup.classList.remove('error');
formGroup.classList.add('success');
errorSpan.textContent = '';
}
3. Візуальні стилі:
.form-group.error input {
border-color: #e74c3c;
}
.form-group.success input {
border-color: #2ecc71;
}
.error-message {
color: #e74c3c;
font-size: 0.85rem;
margin-top: 0.25rem;
min-height: 1.2em; /* щоб макет не стрибав */
}
Частина 4: Відправка форми та localStorage
Вимоги
1. Обробка submit:
form.addEventListener('submit', (e) => {
e.preventDefault();
// Провалідувати ВСІ поля
const isValid = validateAllFields();
if (isValid) {
saveUser();
showSuccessMessage();
form.reset();
clearValidationStyles();
}
});
2. Збереження в localStorage:
- Зберігай масив зареєстрованих користувачів
- Кожен запис:
{ name, email, age, registeredAt } - Пароль НЕ зберігаємо (навіть у localStorage це погана практика)
function saveUser() {
const users = JSON.parse(localStorage.getItem('users') || '[]');
users.push({
name: document.getElementById('name').value.trim(),
email: document.getElementById('email').value.trim(),
age: Number(document.getElementById('age').value),
registeredAt: new Date().toLocaleString('uk-UA'),
});
localStorage.setItem('users', JSON.stringify(users));
renderUsers(); // оновити список знизу
}
3. Повідомлення про успіх:
- Після збереження покажи зелене повідомлення "Реєстрація успішна!" над або під формою
- Повідомлення зникає через 3 секунди
Частина 5: Відображення користувачів
Під формою покажи список зареєстрованих користувачів з localStorage.
function renderUsers() {
const users = JSON.parse(localStorage.getItem('users') || '[]');
const container = document.getElementById('users-list');
if (users.length === 0) {
container.innerHTML = '<p>Поки немає зареєстрованих користувачів</p>';
return;
}
container.innerHTML = users
.map(user => `
<div class="user-card">
<strong>${user.name}</strong>
<span>${user.email}</span>
<span>Вік: ${user.age}</span>
<small>Зареєстровано: ${user.registeredAt}</small>
</div>
`)
.join('');
}
// Відобразити при завантаженні сторінки
renderUsers();
Частина 6 (бонус): Індикатор складності пароля
Додай візуальний індикатор сили пароля.
Рівні
| Рівень | Умова | Колір |
|---|---|---|
| Слабкий | Менше 8 символів | Червоний |
| Середній | 8+ символів, є цифра | Жовтий |
| Сильний | 8+ символів, цифра + спецсимвол + велика літера | Зелений |
function getPasswordStrength(password) {
let strength = 0;
if (password.length >= 8) strength++;
if (/\d/.test(password)) strength++;
if (/[!@#$%^&*(),.?":{}|<>]/.test(password)) strength++;
if (/[A-Z]/.test(password)) strength++;
if (password.length >= 12) strength++;
if (strength <= 1) return { level: 'weak', label: 'Слабкий', color: '#e74c3c' };
if (strength <= 3) return { level: 'medium', label: 'Середній', color: '#f39c12' };
return { level: 'strong', label: 'Сильний', color: '#2ecc71' };
}
Візуалізація:
- Прогрес-бар під полем пароля (ширина залежить від рівня)
- Текстовий лейбл ("Слабкий", "Середній", "Сильний")
- Оновлюється на кожен
input
Підказки
novalidateна формі вимикає вбудовану HTML5-валідаціюe.preventDefault()в обробникуsubmitзапобігає перезавантаженню сторінкиinput.closest('.form-group')шукає найближчого батька з таким класом- Для перевірки email достатньо простого regex:
/^[^\s@]+@[^\s@]+\.[^\s@]+$/ JSON.parse(localStorage.getItem('key') || '[]')— безпечне зчитування масивуsetTimeout(() => message.remove(), 3000)— видалити повідомлення через 3 секунди- Не зберігай пароль в localStorage навіть у навчальному проєкті
Критерії оцінки
| Критерій | Бали |
|---|---|
| HTML-форма з усіма полями та правильною структурою | 10 |
| Валідація кожного поля за правилами | 25 |
| Валідація в реальному часі (blur + input) | 15 |
| Візуальне відображення помилок/успіху (CSS) | 10 |
| Збереження в localStorage та відображення користувачів | 25 |
| Повідомлення про успішну реєстрацію | 5 |
| Загальна якість коду та стилізації | 10 |
| Бонус: Індикатор складності пароля | +10 |