Перейти к содержимому
Забрать бесплатно
Модификации выключены. Так эта страница выглядит на обычной Тильде.

Как в квизе Тильды показать разный результат по баллам

28 июля 2026 8 МИН КОМАНДА MODKOD

Квиз в Тильде — это форма с вариантами ответа, и заканчивается она тем же, чем любая форма: заявкой. Баллы не складывает ни один её скрипт, ветку результата не выбирает тоже — это придётся сделать своим кодом. Зато есть два готовых механизма, на которые своё считание опирается: скрытое поле, которое доезжает до заявки, и подстановка значений полей в текст. Оба проверены на её же рантайме, и у второго есть условие, о котором в справке не сказано.

✓ ПРОВЕРЕНО 28 июля 2026 что уезжает в заявку при частично отвеченном квизе, доезжает ли скрытое поле с посчитанным баллом, при каких условиях подставляется {{form.Field}} и что печатается на месте неразрешённой переменной — стенд с рантаймом форм Тильды из modkit/stand/vendor, Chromium — вызовами t_serializeArray, window.tildaForm.validate и t_forms__parseVariables

Короткий ответ

Баллы Тильда не считает: в её наборе скриптов такой функции нет. Считать нужно своим кодом, а результат положить в скрытое поле — оно доезжает до заявки. Чтобы показать результат на странице, есть подстановка {{form.Поле}} в текст записи, но обновляется она по событию change: после записи значения кодом его нужно послать вручную.

Из чего на самом деле состоит квиз

Квиз, который считает баллы и по сумме выбирает ответ, — это калькулятор с вариантами вместо полей ввода, и собирается он из тех же частей. Квиз в Тильде — это форма, обёрнутая в .t-quiz. Так её и опознаёт рантайм форм:

function t_forms__isQuiz(t) {
  return t && t.closest('.t-quiz');
}

Отсюда следует одна практическая вещь: квизу не достаётся новый вид сообщения об успехе. В той же функции, что решает, каким показывать блок благодарности, квиз стоит первым в списке исключений:

function t_forms__isNewSuccessBox(t) {
  return !!t && (!t_forms__isQuiz(t) &&
    !['t396','t447','t651','t653','t706','t708','t945','t1122'].some(e => t.closest('.' + e)));
}

То есть экран результата у квиза свой, и подменять его чужими средствами не нужно — нужно наполнять.

Своего кода подсчёта в наборе нет. В снимке рантайма — 53 файла скриптов, и единственное упоминание квиза по имени во всех них одно:

e.querySelector('.t-quiz') && t_onFuncLoad('t_quiz__adjustStepToHeight', function () {
  t_quiz__adjustStepToHeight({ rec: e });
});

Подгонка высоты шага. Ни quizScore, ни calcScore, ни любой другой счётчик по файлам не находится. Условные поля у Тильды есть — рантайм форм уважает атрибут data-hidden-by-condition="true" и выкидывает такие поля из заявки:

function t_forms__getConditionCheckHandler(t) {
  var e = Array.from(t.querySelectorAll('[data-hidden-by-condition="true"]'));
  return { isHiddenByCondition: function (r) {
    return e.some(function (t) { return t === r || t.contains(r) });
  } };
}

Но условие — это сравнение значения одного поля, а не сумма по нескольким. Показать разный текст по ответу на один вопрос настройками можно. Разный по сумме баллов — нет.

Что уезжает в заявку, если ответили не на всё

Замер на стенде с её собственным рантаймом: три группы вариантов, отвечена одна.

ЧтоРезультат
поля в заявкеQ1="да"
поля неотвеченных вопросовотсутствуют, не пустые — ключа нет вовсе
ошибки от window.tildaForm.validateQ2:req, Q3:req

Два вывода. Первый: неотвеченный вопрос не оставляет в заявке следа, и по заявке нельзя отличить «пропустил» от «не было такого вопроса» — если это важно, пишите пропуск сами. Второй: обязательность у группы вариантов держится на одном атрибуте data-tilda-req="1" у первого переключателя, а проверка считает отмеченные по имени поля во всей форме:

var F = r.querySelectorAll('[name="' + b + '"]:checked').length;

Пока имена групп разные, всё честно. Совпали имена — совпадёт и проверка.

Куда положить посчитанный балл

Скрытое поле. Замер: считаем сумму своим кодом, пишем в два скрытых поля — одно внутри штатной группы .t-input-group, второе просто в форме, — и смотрим, что собирает штатный сериализатор.

ПолеВ заявке
Result — в группе с data-field-type="hd"Result="практик"
Score — обычный <input type="hidden"> в формеScore="3"
пустое скрытое полеуезжает как "", а не пропадает

Оба доехали. Сериализатору группа не нужна — ему нужны name и отсутствие disabled:

function t_serializeArray(t) {
  for (var e = [], r = t.querySelectorAll('input, textarea, button, select'),
       n = t_forms__getConditionCheckHandler(t).isHiddenByCondition, a = 0; a < r.length; a++)
    if (!(!r[a].name || r[a].disabled ||
          ['file','reset','submit','button'].indexOf(r[a].type) > -1 || n(r[a])))
      …
}

Значит для заявки достаточно самого простого варианта: положить в форму <input type="hidden" name="Result"> и записать туда значение до отправки. Только disabled не ставьте — это единственный способ случайно потерять поле.

Считать удобно в tildaform:beforesend: событие всплывает, поэтому слушать можно на документе, а e.target — это форма.

document.addEventListener('tildaform:beforesend', function (e) {
  var form = e.target;
  var score = 0;
  ['Q1', 'Q2', 'Q3'].forEach(function (name) {
    var checked = form.querySelector('[name="' + name + '"]:checked');
    if (checked && checked.value === 'да') score += 1;
  });
  var field = form.querySelector('[name="Result"]');
  if (field) {
    field.value = score >= 3 ? 'практик' : score >= 2 ? 'середина' : 'новичок';
    field.dispatchEvent(new Event('change', { bubbles: true }));
  }
});

Одно предупреждение про это событие: оно не отменяемое. Проверено по способу, которым Тильда его создаёт, — initEvent(e, !0, !1): всплывает, но preventDefault на нём ничего не значит. Считать в нём можно, останавливать отправку — нет.

Показать результат на странице: подстановка и её условие

У Тильды есть родная подстановка значений полей в текст. Регулярное выражение, которым она ищет переменные, поддерживает и значение по умолчанию:

function t_forms__getVariableRegexp() {
  return /{{(form|tilda).([A-zА-я0-9_-]+)(?:=([^{}]+))?}}/g;
}

То есть {{form.Result}}, {{tilda.lead_id}} и {{form.Result=пока нет}}. Каждая найденная переменная заменяется на <span class="t-variable-placeholder">, и дальше в него пишется значение поля — найденного по группе:

function t_forms__findFormField(t, e) {
  var r = t.querySelector('.t-input-group[data-field-name="' + e + '"]');
  …
}

Здесь и лежат оба подводных камня. Замер, четыре случая на одном стенде:

Как устроено полеЧто напечаталось
видимое текстовое поле в штатной группеИван — значение поля
скрытое поле в группе с data-input-lid, значение в разметкепрактик
скрытое поле в группе без data-input-lidResult — само имя переменной
переменная, для которой поля нет вовсеScore — само имя переменной
{{form.Missing=пока нет}}, поля нетпока нет — значение по умолчанию

Первый камень: группа обязана иметь все три атрибута — data-input-lid, data-field-type, data-field-name. Без первого инициализация поля падает с Please, set all the required data-attrs to initialize FormInput. в консоли, а внешняя операция при этом успешно завершается: на странице просто печатается имя переменной, и ничего не сломано настолько, чтобы это заметить. Печатает его последняя строка подстановки — значение, иначе значение по умолчанию, иначе имя:

t.textContent = r || n || o;

Второй камень серьёзнее, и он ровно про наш случай — значение, записанное кодом:

МоментЧто в тексте
сразу после подстановки, поле пустоеResult
значение записано кодом, событий нетResult
после dispatchEvent(new Event('change', {bubbles: true}))практик

Причина видна в том же файле: поле подписывается на события своей группы, и подстановка обновляется только по ним.

e.addEventListener('change', _);
…
o.onChange(function () { return n(t) }, { uniqueKey: r.name });

Присваивание input.value = … из кода событий не создаёт — это свойство DOM, а не действие человека. Отсюда правило: записали значение своим кодом — пошлите полю change с bubbles: true. Одна строка, и подстановка начинает работать.

Порядок, который работает

  1. В квизе — обычные группы вариантов, имена вопросов разные.
  2. В форму добавлено скрытое поле под результат. Если результат нужно ещё и показать текстом, оно лежит в штатной группе со всеми тремя атрибутами.
  3. На tildaform:beforesend свой код считает сумму, выбирает ветку, пишет значение в поле и посылает полю change.
  4. Текст результата на странице собран из {{form.Result}} — со значением по умолчанию на случай, если посетитель дошёл до текста мимо формы.
  5. В заявку уезжают и ответы, и посчитанный результат: по заявке видно не только что человек нажимал, но и что ему показали.

Ветку можно выбирать не только по сумме. Всё, что доступно коду в момент beforesend, — набор отмеченных вариантов, время на прохождение, порядок ответов. Считать по сумме — самый простой случай, а не единственный.

Что проверить у себя

// 1. Квиз ли это вообще — есть ли обёртка, по которой его узнаёт рантайм
document.querySelectorAll('.t-quiz').length;

// 2. Имена вопросов не должны совпадать
[...document.querySelectorAll('.t-form [data-field-name]')]
  .map(g => g.getAttribute('data-field-name'));

// 3. Что уедет в заявку прямо сейчас
t_serializeArray(document.querySelector('.t-form'))
  .map(f => f.name + '=' + f.value);

// 4. Все три атрибута у группы результата
(g => g && ['data-input-lid','data-field-type','data-field-name']
  .map(a => a + '=' + g.getAttribute(a)))(
  document.querySelector('.t-input-group[data-field-name="Result"]'));

// 5. Живые переменные в тексте и то, что в них попало
[...document.querySelectorAll('.t-variable-placeholder')]
  .map(p => p.dataset.variable + ' → ' + p.textContent);

Если в пятой проверке имя переменной равно её значению — подстановка не сработала, и смотреть надо на третью и четвёртую.

Свободные моды библиотеки — в каталоге. Если квиз должен не показать картинку, а посчитать смету, отправить результат в CRM разными путями по ветке или собрать шаг из ответов на предыдущие — это уже работа под задачу: доработка форм.

Вопросы по этой статье 3

В наборе скриптов, которые Тильда грузит на страницу с квизом, нет ни одной функции подсчёта: единственное, что вызывается по имени квиза, — подгонка высоты шага под содержимое. Ветвление по ответу на один вопрос настроить можно (условные поля), сумму баллов по нескольким вопросам — нет.