Розробка покрокової форми заявки на 1С-Бітрікс
Довга форма на одній сторінці вбиває конверсію — це факт, підтверджений нашими A/B тестами на 40+ проектах. Користувач бачить 10+ полів і закриває вкладку за 5 секунд. Рішення — розбити анкету на кроки по 2–4 поля. Кожен екран здається простим, прогрес видно, конверсія зростає на 30–50%. Ми розробляємо кастомні компоненти для Бітрікс та Бітрікс24 з клієнтською навігацією, валідацією на кожному кроці та фінальним відправленням в CRM. В основі лежить штатний механізм компонентів 2.0 та інфоблоків v2.0. Серверна частина створює ліди через CCrmLead, підтримує бізнес-процеси та REST API. Для довгих форм впроваджуємо збереження прогресу в localStorage, щоб користувач не втратив дані при випадковому закритті. Такий підхід уже приніс клієнтам зростання конверсії в 2–3 рази в середньому по 20 проектах.
Ось порівняння підходів:
| Характеристика | Одноекранна форма | Покрокова форма |
|---|---|---|
| Конверсія | 2–5% | 8–15% |
| Відмови (bounce) | 30–50% | 10–20% |
| Час заповнення | 3–5 хв | 1–2 хв (суб'єктивно) |
| Юзабіліті | низьке | високе |
Чому покрокова форма збільшує конверсію?
Прогрес-бар та розбивка на кроки психологічно знижують поріг входу. Користувач бачить «всього 3 кроки» і готовий почати. Кожен завершений крок дає відчуття прогресу. На практиці ми фіксуємо зростання заповнюваності на 40% порівняно з одноекранною формою. Крім того, валідація на кожному кроці виключає помилки в кінці. Додатково, покрокова форма дозволяє збирати більш якісні ліди: користувач не втомлюється і вводить дані уважніше. Це особливо важливо для B2B-заявок з технічними полями.
Які технічні проблеми виникають при реалізації?
Основні складнощі — синхронізація стану між кроками, обробка помилок на сервері після фінального відправлення та коректна валідація з масками введення. Якщо користувач ввів телефон з маскою, а на сервері перевірка строгіша — лід може не створитися. Ми вирішуємо це через єдину валідацію на клієнті та сервері. Ще одна проблема — втрата даних при закритті браузера. Ми впроваджуємо збереження в localStorage з таймаутом 30 хвилин. Сертифіковані спеціалісти нашої команди мають досвід вирішення цих проблем на 50+ проектах.
Як влаштований компонент покрокової форми?
Форма зберігає стан цілком на клієнті (JS-об'єкт), відправляє дані єдиним запитом після останнього кроку. Сервер отримує повний набір даних.
Конфігурація кроків — в параметрах компонента (PHP-масив) або в адміністративному інтерфейсі через інфоблок.
// /local/components/local/multistep.form/class.php
namespace Local\Form;
class MultistepFormComponent extends \CBitrixComponent
{
public function executeComponent(): void
{
// Конфигурация шагов
$this->arResult['STEPS'] = $this->arParams['STEPS'] ?? $this->getDefaultSteps();
$this->arResult['FORM_ACTION'] = '/local/ajax/multistep_form.php';
$this->arResult['SESSID'] = bitrix_sessid();
$this->includeComponentTemplate();
}
private function getDefaultSteps(): array
{
return [
[
'id' => 'step_personal',
'title' => 'Ваши данные',
'fields' => [
['name' => 'name', 'label' => 'Имя', 'type' => 'text', 'required' => true],
['name' => 'phone', 'label' => 'Телефон', 'type' => 'tel', 'required' => true],
['name' => 'email', 'label' => 'Email', 'type' => 'email', 'required' => false],
],
],
[
'id' => 'step_details',
'title' => 'Детали заявки',
'fields' => [
['name' => 'service', 'label' => 'Услуга', 'type' => 'select', 'options' => ['Консультация', 'Аудит', 'Разработка']],
['name' => 'budget', 'label' => 'Бюджет', 'type' => 'radio', 'options' => ['до 100к', '100–500к', 'от 500к']],
],
],
[
'id' => 'step_message',
'title' => 'Комментарий',
'fields' => [
['name' => 'message', 'label' => 'Опишите задачу', 'type' => 'textarea', 'required' => false],
],
],
];
}
}
Клієнтська логіка
class MultistepForm {
constructor(formEl) {
this.form = formEl;
this.steps = Array.from(formEl.querySelectorAll('.form-step'));
this.current = 0;
this.formData = {};
this.bindEvents();
this.showStep(0);
}
bindEvents() {
this.form.querySelectorAll('.btn-next').forEach(btn =>
btn.addEventListener('click', () => this.tryNext())
);
this.form.querySelectorAll('.btn-prev').forEach(btn =>
btn.addEventListener('click', () => this.prev())
);
this.form.addEventListener('submit', e => {
e.preventDefault();
this.submit();
});
}
tryNext() {
if (!this.validateCurrentStep()) return;
this.collectCurrentStepData();
if (this.current < this.steps.length - 1) {
this.showStep(this.current + 1);
}
}
prev() {
if (this.current > 0) {
this.showStep(this.current - 1);
}
}
showStep(index) {
this.steps.forEach((step, i) => {
step.classList.toggle('active', i === index);
});
this.current = index;
this.updateProgress();
}
validateCurrentStep(): boolean {
const stepEl = this.steps[this.current];
let valid = true;
stepEl.querySelectorAll('[required]').forEach(field => {
const error = stepEl.querySelector(`[data-error-for="${field.name}"]`);
if (!field.value.trim()) {
valid = false;
field.classList.add('error');
if (error) error.textContent = 'Это поле обязательно';
} else {
field.classList.remove('error');
if (error) error.textContent = '';
}
});
// Валидация телефона
const phoneField = stepEl.querySelector('[name="phone"]');
if (phoneField && phoneField.value) {
const phoneRegex = /^(\+7|7|8)?[\s-]?\(?\d{3}\)?[\s-]?\d{3}[\s-]?\d{2}[\s-]?\d{2}$/;
if (!phoneRegex.test(phoneField.value.replace(/\s/g, ''))) {
valid = false;
phoneField.classList.add('error');
}
}
return valid;
}
collectCurrentStepData() {
const stepEl = this.steps[this.current];
stepEl.querySelectorAll('input, select, textarea').forEach(field => {
if (field.type === 'radio' || field.type === 'checkbox') {
if (field.checked) this.formData[field.name] = field.value;
} else {
this.formData[field.name] = field.value;
}
});
}
updateProgress() {
const percent = ((this.current) / (this.steps.length - 1)) * 100;
const bar = this.form.querySelector('.progress-bar-fill');
if (bar) bar.style.width = Math.min(100, percent) + '%';
const label = this.form.querySelector('.progress-label');
if (label) label.textContent = `Шаг ${this.current + 1} из ${this.steps.length}`;
}
async submit() {
this.collectCurrentStepData();
const submitBtn = this.form.querySelector('[type="submit"]');
submitBtn.disabled = true;
submitBtn.textContent = 'Отправляем...';
try {
const response = await fetch(this.form.dataset.action, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
...this.formData,
sessid: this.form.dataset.sessid,
}),
});
const result = await response.json();
if (result.success) {
this.showSuccessScreen();
} else {
this.showError(result.error || 'Ошибка отправки');
submitBtn.disabled = false;
submitBtn.textContent = 'Отправить';
}
} catch (e) {
this.showError('Ошибка соединения. Попробуйте позже.');
submitBtn.disabled = false;
}
}
showSuccessScreen() {
this.form.innerHTML = `
<div class="form-success">
<h3>Спасибо! Мы свяжемся с вами в течение часа.</h3>
</div>
`;
}
showError(message) {
let errEl = this.form.querySelector('.form-global-error');
if (!errEl) {
errEl = document.createElement('div');
errEl.className = 'form-global-error';
this.steps[this.current].prepend(errEl);
}
errEl.textContent = message;
}
}
document.querySelectorAll('.multistep-form').forEach(el => new MultistepForm(el));
Серверний обробник
// /local/ajax/multistep_form.php
\Bitrix\Main\Loader::includeModule('crm');
$data = json_decode(file_get_contents('php://input'), true);
$sessid = $data['sessid'] ?? '';
if (!check_bitrix_sessid($sessid)) {
http_response_code(403);
echo json_encode(['error' => 'Forbidden']);
exit;
}
$name = htmlspecialchars(trim($data['name'] ?? ''));
$phone = htmlspecialchars(trim($data['phone'] ?? ''));
$email = htmlspecialchars(trim($data['email'] ?? ''));
$service = htmlspecialchars($data['service'] ?? '');
$budget = htmlspecialchars($data['budget'] ?? '');
$message = htmlspecialchars($data['message'] ?? '');
if (empty($name) || empty($phone)) {
echo json_encode(['error' => 'Не заполнены обязательные поля']);
exit;
}
$comments = implode("\n", array_filter([
$service ? "Услуга: {$service}" : '',
$budget ? "Бюджет: {$budget}" : '',
$message ? "Комментарий: {$message}" : '',
]));
$lead = new \CCrmLead(false);
$leadId = $lead->Add([
'TITLE' => 'Заявка с сайта — ' . $name,
'NAME' => $name,
'PHONE' => [['VALUE' => $phone, 'VALUE_TYPE' => 'WORK']],
'EMAIL' => [['VALUE' => $email, 'VALUE_TYPE' => 'WORK']],
'SOURCE_ID' => 'WEB',
'COMMENTS' => $comments,
]);
echo json_encode(['success' => (bool)$leadId]);
Збереження прогресу: як не втратити дані?
Для довгих форм (5+ кроків) ми додаємо збереження в localStorage — при випадковому закритті дані відновлюються на тому ж кроці. Таймаут — 30 хвилин, далі дані вважаються застарілими.
// После каждого шага
localStorage.setItem('form_progress', JSON.stringify({
step: this.current,
data: this.formData,
savedAt: Date.now(),
}));
// При инициализации — восстановить, если не старше 30 минут
const saved = JSON.parse(localStorage.getItem('form_progress') || 'null');
if (saved && Date.now() - saved.savedAt < 1800000) {
this.formData = saved.data;
this.showStep(saved.step);
}
Покрокова інструкція з налаштування
- Створіть новий компонент в
local/components/company/multistep.form. - Визначте масив кроків з полями (як у прикладі вище).
- Реалізуйте клієнтську логіку на JavaScript з валідацією.
- Напишіть серверний обробник для створення лідів.
- Підключіть компонент на сторінку та налаштуйте параметри.
Типові помилки при впровадженні
- Відсутність CSRF-захисту — використовуйте
bitrix_sessid(). - Невідповідність типів даних на клієнті та сервері.
- Ігнорування мобільної версії — перевіряйте адаптивність кроків.
Етапи розробки
- Аналітика — визначення необхідних полів та логіки кроків.
- Проектування — підготовка макетів та конфігурації компонента.
- Розробка — створення PHP-компонента та JS-файлу.
- Тестування — A/B тести, перевірка валідації, обробки помилок.
- Деплой — викатка на бойовий сайт та інтеграція з CRM.
Що входить в роботу
- Документація по API компонента та інструкція з налаштування.
- Доступ до репозиторію з вихідним кодом.
- Навчання менеджерів роботі з формою (заповнення, редагування кроків).
- Гарантійна підтримка протягом 2 місяців після деплою.
Терміни та вартість
| Варіант | Склад | Орієнтовний термін |
|---|---|---|
| Базова покрокова форма | 2–3 кроки, валідація, лід в CRM | 2–4 дні |
| З кастомними полями | Вибір, радіо, файл, динамічні поля | 4–7 днів |
| Повний компонент | Конфігуровані кроки, A/B, аналітика, збереження | 8–12 днів |
Вартість розраховується індивідуально — вона залежить від кількості кроків, складності валідації, необхідності інтеграцій. Зв'яжіться з нами для попередньої оцінки під ваш проект. Замовте розробку покрокової форми, щоб підвищити конверсію вашого сайту. Отримайте консультацію — ми проаналізуємо вашу форму і запропонуємо оптимальне рішення.







