Вебформа Битрикс: своя вёрстка, Ajax через endpoint и параметры AJAX_MODE
Сначала настройте форму в админке
Для связи с лидами удобен модуль Веб‑формы. Возьмём анкету «Оставить заявку» с полями имя, телефон и комментарий.
В настройках модуля стоит отключить упрощённый режим конструктора: иначе нередко всплывают сбои почтовых шаблонов и сохранения шаблонов писем. После сохранения настройки откройте Сервисы → Веб‑формы → Настройка форм, создайте форму во вкладке «Свойства», для группы «Все пользователи» включите действие по заполнению, сохраните, затем перейдите на вкладку «Вопросы».
Создайте вопросы с символьными кодами под ваш контур (ниже условный набор полей).
- APPLICANT_NAME — текстовое поле, обязательное, подпись «Как к вам обращаться», в ответе поле пробел в качестве подписи, тип
text. - LINE_NUMBER — текстовое, обязательное, заголовок «Телефон», параметры ответа
phone. - NOTICE_BODY — многострочное текстовое, необязательное, параметры можно оставить пустыми (в оригинальных инструкциях иногда ошибочно копируют
phoneв textarea).
Если статус результата не появился, добавьте дефолтный вроде Fresh и выдайте на все операции роль создателя результата.
Вывод через form.result.new
Чтобы управлять вёрсткой, подключите компонент на странице или во временном файле, скопируйте вызов в целевой шаблон. В параметрах включите расширенные ошибки, отключите ЧПУ, очистите поля перехода на успех или списки результатов если ответ будет на той же странице.
В режиме правки скопируйте пользовательский шаблон компонента в активный сайт и оставьте в каталоге единственный template.php — так проще поддерживать стили и скрипты.
Структура данных в шаблоне
Основные ключи массива $arResult:
isFormErrorsиisFormNoteравныYпри ошибках и успешном добавлении.FORM_HEADER/FORM_FOOTER— открытие и закрытие тегов формы и служебные скрытые поля.QUESTIONS— вопросы по символьному SID; у каждого естьCAPTION,REQUIRED,HTML_CODE,STRUCTURE.arrVALUES— сохранённые значения при повторном выводе после ошибки.- Для капчи понадобятся
isUseCaptcha, изображение и поле ввода (отдельно ниже).
Собственное оформление template.php
Логика: при успешной отправке можно показыть только текст благодарности, иначе выводится форма. Скрытое поле web_form_submit=Y сообщает модулю веб‑формы применить проверку и сохранить результат. Сообщение об ошибках имеет смысл выводить во вложении с атрибутом role="alert"; для AJAX ниже понадобится контейнер с атрибутом datawf-feedback-slot вместо жёстко пришитых классов.
<?php
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) {
die();
}
?>
<?php if ($arResult['isFormNote'] === 'Y'): ?>
<p>Спасибо, сообщение сохранено. Менеджер свяжется с вами.</p>
<?php else: ?>
<?=$arResult['FORM_HEADER']; ?>
<input type="hidden" name="web_form_submit" value="Y">
<p datawf-feedback-slot></p>
<?php if ($arResult['isFormErrors'] === 'Y'): ?>
<div role="alert"><?= $arResult['FORM_ERRORS_TEXT']; ?></div>
<?php endif; ?>
<p>
<label><?= $arResult['QUESTIONS']['APPLICANT_NAME']['CAPTION']; ?><?= ($arResult['QUESTIONS']['APPLICANT_NAME']['REQUIRED'] === 'Y') ? '*' : ''; ?><br>
<?= $arResult['QUESTIONS']['APPLICANT_NAME']['HTML_CODE']; ?>
</label>
</p>
<p>
<label><?= $arResult['QUESTIONS']['LINE_NUMBER']['CAPTION']; ?><?= ($arResult['QUESTIONS']['LINE_NUMBER']['REQUIRED'] === 'Y') ? '*' : ''; ?><br>
<?= $arResult['QUESTIONS']['LINE_NUMBER']['HTML_CODE']; ?>
</label>
</p>
<p>
<label><?= $arResult['QUESTIONS']['NOTICE_BODY']['CAPTION']; ?><br>
<?= $arResult['QUESTIONS']['NOTICE_BODY']['HTML_CODE']; ?>
</label>
</p>
<p><button type="submit"><?= htmlspecialcharsbx((string)$arResult['arForm']['BUTTON']); ?></button></p>
<?= $arResult['FORM_FOOTER']; ?>
<?php endif; ?>Капча по необходимости
Включите капчу в настройках формы и вставьте в шаблон ветку: показ превью, подпись и поле через CAPTCHA_IMAGE и CAPTCHA_FIELD. Для замены изображения кликом подставляют свой <img> на /bitrix/tools/captcha.php с параметром captcha_sid из CAPTCHACode; глобальные параметры рисунка регулируются в модуле CAPTCHA продукта.
Ajax через отдельный endpoint
Вариант с ручным fetch или XMLHttpRequest сохраняет DOM страницы: ответ можно разобрать как JSON без полной замены блока формы модулём. Привязку оформляют после загрузки страницы, передавая адрес маленького PHP‑файла рядом с шаблоном и используя $templateFolder компонента.
function bindWebformAjax(transportForm, targetUrl, feedbackSelector) {
if (!BX || !transportForm) {
return;
}
BX.bind(transportForm, 'submit', BX.proxy(function (evt) {
BX.PreventDefault(evt);
var slot = transportForm.querySelector(feedbackSelector);
if (slot) {
slot.textContent = '';
}
var xhr = new XMLHttpRequest();
xhr.open('POST', targetUrl);
xhr.onload = function () {
if (xhr.status !== 200) {
alert('Ответ сервера: ' + xhr.status);
return;
}
var decoded;
try {
decoded = JSON.parse(xhr.responseText);
} catch (err) {
alert('Некорректный JSON');
return;
}
if (!decoded.success) {
var lines = '';
for (var sid in decoded.errors) {
if (!decoded.errors.hasOwnProperty(sid)) {
continue;
}
lines += decoded.errors[sid] + '\n';
}
if (slot) {
slot.textContent = lines;
}
return;
}
if (slot) {
slot.textContent = '';
}
if (typeof window.showLeadToast === 'function') {
window.showLeadToast();
}
};
xhr.onerror = function () {
alert('Запрос не выполнен');
};
xhr.send(new FormData(transportForm));
}, transportForm, targetUrl, feedbackSelector));
}
BX.ready(function () {
var frm = document.getElementsByName('FORM_SID_REPLACE')[0];
bindWebformAjax(frm, '#AJAX_ROUTE#', '[datawf-feedback-slot]');
});В примере вместо FORM_SID_REPLACE используйте <?= htmlspecialcharsbx((string)$arResult['arForm']['SID']); ?> во встраиваемом скрипте, а маршрут #AJAX_ROUTE# замените на <?= $templateFolder ?>/ajax_handler.php в финальном template.php. Хук успеха здесь именован showLeadToast чтобы не конфликтовать с вашими утилитами.
Обработчик ajax_handler.php
Подключите проолог, модуль форм, проверьте битрикс‑сессию, отдайте JSON и запустите стандартные побочные эффекты сохранения: CRM-события, журнал результатов и почту.
<?php
require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php';
use Bitrix\Main\Loader;
use Bitrix\Main\Web\Json;
Loader::includeModule('form');
if (!check_bitrix_sessid()) {
echo Json::encode(['success' => false, 'errors' => ['sess' => 'Сессия недействительна, перезагрузите страницу']]);
require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/epilog_after.php';
return;
}
$postValues = $_REQUEST;
$formIdentity = $_POST['WEB_FORM_ID'] ?? null;
$violations = CForm::Check($formIdentity, $postValues, false, 'Y', 'Y');
if (count($violations) > 0) {
echo Json::encode(['success' => false, 'errors' => $violations]);
} elseif ($freshId = CFormResult::Add($formIdentity, $_REQUEST)) {
if (class_exists('CFormCRM')) {
CFormCRM::onResultAdded($formIdentity, $freshId);
}
CFormResult::SetEvent($freshId);
CFormResult::Mail($freshId);
echo Json::encode(['success' => true, 'errors' => new \stdClass()]);
} else {
echo Json::encode(['success' => false, 'errors' => $GLOBALS['strError']]);
}
require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/epilog_after.php';
?>Встроенный AJAX_MODE базового класса компонента
Достаточно передать параметры вида AJAX_MODE=Y, отключить скачок и историю (AJAX_OPTION_JUMP, AJAX_OPTION_HISTORY) и включить нужные побочные стили или тени. Ядро оборачивает вывод контейнером и подменяет HTML после отправки.
Минусы режима очевидны: любые обработчики ввода, навешенные один раз после DOMContentLoaded, после авто‑рендера нужно переинициализировать (маски телефона, кастомные селекты). Если разметку формы дублируют в модальном слое средствами вроде lightbox‑клонирования, Ajax заменит скрытый исходный блок, а копию покажите отдельно — либо переместите вызов компонента в общий узел модалки без «расщепления» DOM.
Свои теги вместо автоматического HTML_CODE
Когда недостаточно стандартной разметки ответов, добавьте result_modifier.php анонимной функцией, чтобы не городить дважды одно имя функции после клонирования шаблона. Функция собирает name form_{FIELD_TYPE}_{ID}, подставляет value, атрибут required и произвольные классы через поле параметров.
<?php
if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true) {
die();
}
$arResult['buildGuestMarkup'] = function (array $fieldMeta, array $storedValues): string {
$firstFacet = $fieldMeta['STRUCTURE'][0];
$answerId = (int)$firstFacet['ID'];
$fieldKind = $firstFacet['FIELD_TYPE'];
$inputSlot = sprintf('form_%s_%d', $fieldKind, $answerId);
$posted = htmlspecialcharsbx((string)($storedValues[$inputSlot] ?? ''), ENT_QUOTES);
$needsRequired = ($fieldMeta['REQUIRED'] === 'Y');
$modifierToken = trim((string)$firstFacet['FIELD_PARAM']);
$extraClass = $modifierToken !== '' ? ' ' . $modifierToken : '';
$requiredChunk = $needsRequired ? ' required' : '';
switch ($fieldKind) {
case 'textarea':
return '<textarea class="guest-textarea' . htmlspecialcharsbx($extraClass, ENT_QUOTES) .
'" name="' . htmlspecialcharsbx($inputSlot, ENT_QUOTES) . '" ' . $requiredChunk . '>' .
$posted . '</textarea>';
default:
return '<input class="guest-text' . htmlspecialcharsbx($extraClass, ENT_QUOTES) .
'" type="text" name="' . htmlspecialcharsbx($inputSlot, ENT_QUOTES) .
'" value="' . $posted . '" ' . $requiredChunk . '>';
}
};
?>В теле шаблона замените $arResult['QUESTIONS'][...]['HTML_CODE'] вызовом $arResult['buildGuestMarkup']($arResult['QUESTIONS'][...], $arResult['arrVALUES']) для каждого SID.
На что провериться перед боем
- Символические коды вопросов в PHP совпадают с административными SID.
sessidживёт во всех отправках: либо оставляет компонент, либо передаётся из скрытых полей заголовка формы.- После включения AJAX_MODE перепройдите клиентские плагины ввода и убедитесь, что обработчик вешаете из обратного вызова аякс‑шаблонов или переиспользуете
BX.ajax/BX.readyповторно.
Не хотите копаться сами?
Починю за 1-3 дня. Без предоплаты — оплата по результату.
15+ лет опыта с 1С-Битрикс · Без предоплаты · 7 дней гарантии