Form Guardian для Vanilla JavaScript
Полное руководство по использованию Form Guardian с vanilla JavaScript. Без зависимостей от фреймворков, работает с любой HTML формой.
🚀 Быстрый старт
Установка
npm install @form-guardian/dom
Или через CDN:
<script type="module">
import { attachFormAutosave } from 'https://cdn.jsdelivr.net/npm/@form-guardian/dom/+esm';
</script>
Базовое использование
<!DOCTYPE html>
<html>
<head>
<title>Контактная форма</title>
</head>
<body>
<form id="contact-form">
<input name="name" placeholder="Имя" required />
<input name="email" type="email" placeholder="Email" required />
<textarea name="message" placeholder="Сообщение"></textarea>
<button type="submit">Отправить</button>
</form>
<script type="module">
import { attachFormAutosave } from '@form-guardian/dom';
const form = document.getElementById('contact-form');
const autosave = attachFormAutosave({
formId: 'contact-form',
root: form,
autoRestore: true,
debounceMs: 500,
});
form.addEventListener('submit', async (e) => {
e.preventDefault();
// Очистка черновика после успешной отправки
await autosave.clear();
// Отправка данных формы
const formData = new FormData(form);
await fetch('/api/contact', {
method: 'POST',
body: formData,
});
});
</script>
</body>
</html>
📚 Полный API
attachFormAutosave(options)
Параметры:
formId(string, обязательный) - Уникальный идентификатор формыroot(HTMLFormElement, обязательный) - Элемент формыautoRestore(boolean, опциональный) - Автовосстановление при загрузке страницыdebounceMs(number, опциональный) - Задержка debounce (по умолчанию: 500мс)ttl(object, опциональный) - Время истечения черновикаonBeforeSave- Callback перед сохранениемonAfterSave- Callback после сохраненияonBeforeRestore- Callback перед восстановлениемonAfterRestore- Callback после восстановления
Возвращает: FormAutosaveHandle с методами:
restore()- В осстановить черновикclear()- Очистить черновикdestroy()- Очистка ресурсовhasDraft()- Проверить наличие черновикаgetCurrentValues()- Получить текущие значения формы
🎯 Распространенные паттерны
С индикатором черновика
<div id="draft-indicator" style="display: none;">
📝 Черновик сохранен в <span id="draft-time"></span>
</div>
<form id="my-form">
<!-- поля формы -->
</form>
<script type="module">
import { attachFormAutosave } from '@form-guardian/dom';
const form = document.getElementById('my-form');
const indicator = document.getElementById('draft-indicator');
const timeSpan = document.getElementById('draft-time');
const autosave = attachFormAutosave({
formId: 'my-form',
root: form,
autoRestore: true,
onAfterSave: async () => {
// Показать индикатор
indicator.style.display = 'block';
timeSpan.textContent = new Date().toLocaleTimeString();
// Скрыть через 3 секунды
setTimeout(() => {
indicator.style.display = 'none';
}, 3000);
},
});
</script>
С подтверждением восстановления
import { attachFormAutosave } from '@form-guardian/dom';
const form = document.getElementById('my-form');
const autosave = attachFormAutosave({
formId: 'my-form',
root: form,
autoRestore: false, // Ручное восстановление
});
// Проверка и запрос у пользователя
async function checkForDraft() {
const hasDraft = await autosave.hasDraft();
if (hasDraft) {
const meta = await autosave.getDraftMeta();
const shouldRestore = confirm(
`Найден черновик от ${new Date(meta.updatedAt).toLocaleString()}. Восстановить?`
);
if (shouldRestore) {
await autosave.restore();
} else {
await autosave.clear();
}
}
}
checkForDraft();
Многостраничная форма
// Страница 1
const step1Form = document.getElementById('step1');
const step1Autosave = attachFormAutosave({
formId: 'wizard-step1',
root: step1Form,
autoRestore: true,
});
document.getElementById('next-btn').addEventListener('click', async () => {
// Сохранение текущего шага
const values = await step1Autosave.getCurrentValues();
// Переход на следующую страницу
window.location.href = '/step2';
});
// Страница 2
const step2Form = document.getElementById('step2');
const step2Autosave = attachFormAutosave({
formId: 'wizard-step2',
root: step2Form,
autoRestore: true,
});
step2Form.addEventListener('submit', async (e) => {
e.preventDefault();
// Объединение всех шагов
const step1Data = await loadDraftCore('wizard-step1');
const step2Data = await step2Autosave.getCurrentValues();
const completeData = { ...step1Data?.values, ...step2Data };
// Отправка
await fetch('/api/submit', {
method: 'POST',
body: JSON.stringify(completeData),
});
// Очистка всех черновиков
await clearDraft('wizard-step1');
await step2Autosave.clear();
});
С валидацией формы
import { attachFormAutosave } from '@form-guardian/dom';
const form = document.getElementById('my-form');
const autosave = attachFormAutosave({
formId: 'my-form',
root: form,
autoRestore: true,
onBeforeSave: (values) => {
// Валидация перед сохранением
console.log('Валидация перед сохранением:', values);
},
onAfterRestore: (values) => {
// Запуск валидации после восстановления
form.querySelectorAll('input').forEach(input => {
input.dispatchEvent(new Event('blur'));
});
},
});
// Кастомная валидация
form.addEventListener('submit', async (e) => {
e.preventDefault();
const values = await autosave.getCurrentValues();
// Валидация
if (!values.name || values.name.length < 2) {
alert('Имя должно быть не менее 2 символов');
return;
}
if (!values.email || !values.email.includes('@')) {
alert('Неверный email');
return;
}
// Отправка
await autosave.clear();
form.submit();
});
🔧 Продвинутые возможности
Отслеживание аналитики
const autosave = attachFormAutosave({
formId: 'my-form',
root: form,
onBeforeSave: (values) => {
gtag('event', 'draft_saving', { form_id: 'my-form' });
},
onAfterSave: (values) => {
gtag('event', 'draft_saved', { form_id: 'my-form' });
},
onAfterRestore: (values) => {
gtag('event', 'draft_restored', { form_id: 'my-form' });
},
});
Безопасность - Исключение чувствительных полей
const autosave = attachFormAutosave({
formId: 'payment-form',
root: form,
blacklist: [
'input[type="password"]',
'input[name="cvv"]',
'input[name="cardNumber"]',
],
});
TTL - Автоматическое истечение черновиков
const autosave = attachFormAutosave({
formId: 'my-form',
root: form,
ttl: { days: 7 }, // Истекает через 7 дней
onDraftExpired: (draftId) => {
console.log('Черновик истек:', draftId);
document.getElementById('draft-expired-msg').style.display = 'block';
},
});
Пакетное сохранение
const autosave = attachFormAutosave({
formId: 'my-form',
root: form,
batchSaveInterval: 5000, // Сохранять каждые 5 секунд
debounceMs: 300,
});
🎨 Примеры UI
Бейдж статуса черновика
<style>
.draft-status {
position: fixed;
top: 10px;
right: 10px;
padding: 8px 16px;
background: #4caf50;
color: white;
border-radius: 4px;
opacity: 0;
transition: opacity 0.3s;
}
.draft-status.show {
opacity: 1;
}
</style>
<div class="draft-status" id="status">Черновик сохранен</div>
<script type="module">
const status = document.getElementById('status');
const autosave = attachFormAutosave({
formId: 'my-form',
root: document.getElementById('my-form'),
onAfterSave: () => {
status.classList.add('show');
setTimeout(() => status.classList.remove('show'), 2000);
},
});
</script>
Диалог восстановления
<dialog id="restore-dialog">
<p>Найден несохраненный черновик. Восстановить?</p>
<button id="restore-yes">Да</button>
<button id="restore-no">Нет</button>
</dialog>
<script type="module">
const dialog = document.getElementById('restore-dialog');
const autosave = attachFormAutosave({
formId: 'my-form',
root: form,
autoRestore: false,
});
async function checkDraft() {
if (await autosave.hasDraft()) {
dialog.showModal();
}
}
document.getElementById('restore-yes').onclick = async () => {
await autosave.restore();
dialog.close();
};
document.getElementById('restore-no').onclick = async () => {
await autosave.clear();
dialog.close();
};
checkDraft();
</script>