Перейти к основному содержимому

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() - Получить текущие значения формы

Полный справочник API →

🎯 Распространенные паттерны

С индикатором черновика

<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>

🔗 Связанные ресурсы