> ## Documentation Index
> Fetch the complete documentation index at: https://docs.landerlab.io/llms.txt
> Use this file to discover all available pages before exploring further.

# События Quiz API: отслеживание и автоматизация квиза

> События квиза на window: загрузка, показ шага, смена ответа, ошибка валидации и отправка. Подключайте аналитику и запускайте свой JavaScript-код.

Ваш квиз сообщает о происходящем через события браузера на `window`. Подписывайтесь на них, чтобы отслеживать поведение в аналитике, реагировать на ответы или запускать собственный код в нужный момент.

```javascript theme={null}
window.addEventListener('ll-quiz-step-view', (event) => {
  console.log('Visitor is on step', event.detail.stepName);
});
```

Каждое событие передаёт данные в `event.detail`. Всегда доступны два поля:

* `quizId` — какой квиз вызвал событие
* `llQuizApi` — [Quiz API](/ru/features/quizzes/api/reference), с помощью которого прямо в обработчике можно читать или менять квиз

<Note>
  `ll-quiz-init` срабатывает, как только квиз готов, — это может произойти раньше, чем выполнится ваш скрипт. Не переживайте: обработчик, зарегистрированный позже, всё равно получит `ll-quiz-init`. Все остальные события срабатывают по действиям посетителя, когда ваш скрипт уже на месте.
</Note>

## События кратко

| Событие | Когда срабатывает | Полезные данные |
| :- | :- | :- |
| `ll-quiz-init` | Квиз готов | `llQuizApi` |
| `ll-quiz-step-view` | Показан шаг | `stepName`, `fields` |
| `ll-quiz-step-leave` | Посетитель покидает шаг | `stepName` |
| `ll-quiz-value-change` | Ответ зафиксирован | `blockName`, `value`, `previousValue`, `source` |
| `ll-quiz-input-click` | Нажат вариант ответа | `blockName`, `optionLabel`, `optionValue` |
| `ll-quiz-button-click` | Нажата кнопка | `blockName` |
| `ll-quiz-validation-error` | Переход с шага заблокирован | `errors`, `source` |
| `ll-quiz-google-address-select` | Выбран адрес | `address` |
| `ll-quiz-submit` | Квиз отправлен | `fields`, `fieldsSimple`, `response` |
| `ll-quiz-exit` | Вкладка закрыта | |

## ll-quiz-init

Срабатывает один раз, когда квиз появился на странице и готов к работе. Здесь удобно получить `llQuizApi`, предзаполнить значения или выполнить начальную настройку.

<ResponseField name="quizId" type="string" />

<ResponseField name="llQuizApi" type="LlQuizApi">
  Quiz API для этого квиза
</ResponseField>

```javascript theme={null}
window.addEventListener('ll-quiz-init', (event) => {
  const quiz = event.detail.llQuizApi;
  quiz.getBlock('source').setValue('landing-page-a');
});
```

## ll-quiz-step-view

Срабатывает каждый раз, когда показывается шаг, включая первый шаг при загрузке и возврат посетителя назад.

<ResponseField name="stepId" type="string" />

<ResponseField name="stepName" type="string" />

<ResponseField name="fields" type="Field[]">
  Ответы, данные к этому моменту, см. раздел «fields и fieldsSimple» ниже
</ResponseField>

<ResponseField name="fieldsSimple" type="object">
  Те же ответы в формате «метка → значение»
</ResponseField>

```javascript theme={null}
window.addEventListener('ll-quiz-step-view', (event) => {
  const { stepName, quizId } = event.detail;
  window.dataLayer?.push({ event: 'quiz_step', quiz: quizId, step: stepName });
});
```

## ll-quiz-step-leave

Срабатывает, когда посетитель уходит с шага, до следующего `ll-quiz-step-view`. Используйте его, чтобы измерить, сколько времени занял шаг, или сохранить черновик.

<ResponseField name="stepName" type="string" />

```javascript theme={null}
let shownAt = Date.now();

window.addEventListener('ll-quiz-step-view', () => {
  shownAt = Date.now();
});

window.addEventListener('ll-quiz-step-leave', (event) => {
  const seconds = Math.round((Date.now() - shownAt) / 1000);
  window.dataLayer?.push({ event: 'quiz_step_time', step: event.detail.stepName, seconds });
});
```

## ll-quiz-value-change

Срабатывает, когда ответ зафиксирован. Поля с вводом (текст, email, телефон, число, textarea, дата, дата рождения, почтовый индекс, адрес, OTP) фиксируются, когда посетитель покидает поле или шаг, поэтому вы получаете одно событие на ответ, а не на каждое нажатие клавиши. Все остальные поля и любое значение, заданное через API, фиксируются сразу.

<ResponseField name="blockId" type="string" />

<ResponseField name="blockName" type="string">
  Имя блока из конструктора
</ResponseField>

<ResponseField name="blockType" type="string">
  Например, `text-field` или `multiple-choice`
</ResponseField>

<ResponseField name="stepId" type="string" />

<ResponseField name="stepName" type="string" />

<ResponseField name="value" type="string">
  Новый ответ в том же формате, что и `Field.value`
</ResponseField>

<ResponseField name="previousValue" type="string">
  Ответ, о котором сообщалось в прошлый раз, или значение, с которым загрузился квиз
</ResponseField>

<ResponseField name="source" type="'user' | 'api' | 'system'">
  `user` — посетитель. `api` — ваш скрипт через Quiz API. `system` — сам квиз, например очистка ответов после отправки
</ResponseField>

Если значение не изменилось, событие не срабатывает. Значения, которые уже есть при загрузке квиза (значения по умолчанию, параметры URL, восстановленные ответы), считаются исходной точкой и событие не вызывают; они находятся в `fields` события `ll-quiz-step-view`.

```javascript theme={null}
window.addEventListener('ll-quiz-value-change', (event) => {
  const { blockName, value, source } = event.detail;
  if (source !== 'user') return; // ignore scripted and automatic changes

  window.dataLayer?.push({ event: 'quiz_answer', field: blockName, value });
});
```

## ll-quiz-input-click

Срабатывает, когда посетитель нажимает вариант в блоке multiple choice или image choice. Срабатывает раньше `ll-quiz-value-change` для того же клика.

<ResponseField name="blockId" type="string" />

<ResponseField name="blockName" type="string" />

<ResponseField name="stepId" type="string" />

<ResponseField name="stepName" type="string" />

<ResponseField name="optionId" type="string" />

<ResponseField name="optionValue" type="string">
  Значение варианта, как оно хранится в ответе
</ResponseField>

<ResponseField name="optionLabel" type="string">
  Текст, который видел посетитель
</ResponseField>

```javascript theme={null}
window.addEventListener('ll-quiz-input-click', (event) => {
  const { blockName, optionLabel, optionValue, llQuizApi } = event.detail;

  window.dataLayer?.push({ event: 'quiz_option', field: blockName, option: optionLabel });

  // Show a hint under the plan picker for one option
  if (blockName === 'plan') {
    const hint = llQuizApi.getBlock('planHint');
    optionValue === 'enterprise' ? hint.visibility.show() : hint.visibility.hide();
  }
});
```

## ll-quiz-button-click

Срабатывает при нажатии на блок Continue, Previous, Submit или Button — до любой валидации. Срабатывает, даже если клик в итоге заблокирован, поэтому считает попытки, а не успешные переходы.

<ResponseField name="blockId" type="string" />

<ResponseField name="blockName" type="string" />

<ResponseField name="stepId" type="string" />

<ResponseField name="stepName" type="string" />

```javascript theme={null}
window.addEventListener('ll-quiz-button-click', (event) => {
  const { blockName, stepName } = event.detail;
  window.dataLayer?.push({ event: 'quiz_button', step: stepName, button: blockName });
});
```

<Tip>
  Чтобы понять, продвинул ли клик посетителя дальше, сочетайте событие с `ll-quiz-step-leave` (продвинул) или `ll-quiz-validation-error` (не продвинул).
</Tip>

## ll-quiz-validation-error

Срабатывает, когда попытка покинуть шаг заблокирована, потому что что-то на нём невалидно. Вы получаете одно событие на попытку — после завершения всех проверок (включая серверные проверки полей email, телефона и OTP) — со списком ровно тех ошибок, которые видит посетитель. Скрытые блоки никогда не включаются.

<ResponseField name="stepId" type="string" />

<ResponseField name="stepName" type="string" />

<ResponseField name="source" type="'user' | 'api' | 'system'">
  `user` — кнопка или вариант с переходом. `api` — ваш скрипт (`validateStep()`, `goNext()`, `goToStep()`, `submit()`). `system` — квиз сам попытался перейти дальше (loader, countdown, оплата)
</ResponseField>

<ResponseField name="triggerBlockId" type="string">
  Кнопка или вариант, запустившие переход; для `api` пусто
</ResponseField>

<ResponseField name="triggerBlockName" type="string">
  Для `api` пусто
</ResponseField>

<ResponseField name="errors" type="array">
  Одна запись на каждый непрошедший блок в порядке следования на шаге: `blockId`, `blockName`, `blockType` и `messages` (показанные тексты; отображается первый)
</ResponseField>

Событие не срабатывает, если шаг валиден, если использована кнопка Previous (она никогда не валидирует) и если клик по варианту не приводит к переходу.

```javascript theme={null}
window.addEventListener('ll-quiz-validation-error', (event) => {
  const { stepName, errors, llQuizApi } = event.detail;

  // Find out which fields stop visitors
  window.dataLayer?.push({ event: 'quiz_blocked', step: stepName, fields: errors.map((e) => e.blockName) });

  // And help them: move the cursor to the first one
  llQuizApi.getBlock(errors[0].blockId).focus();
});
```

## ll-quiz-google-address-select

Срабатывает, когда посетитель выбирает подсказку в блоке Google Address.

<ResponseField name="blockId" type="string" />

<ResponseField name="blockName" type="string" />

<ResponseField name="stepId" type="string" />

<ResponseField name="stepName" type="string" />

<ResponseField name="address" type="object">
  `street`, `city`, `state`, `stateShort`, `postalCode`, `country`, `countryShort`
</ResponseField>

```javascript theme={null}
window.addEventListener('ll-quiz-google-address-select', (event) => {
  const { address, llQuizApi } = event.detail;
  llQuizApi.getBlock('city').setValue(address.city);
});
```

## ll-quiz-submit

Срабатывает, когда квиз отправлен, — после сохранения лида. Это подходящее место, чтобы отправить ответы в другую систему или запустить конверсию.

<ResponseField name="stepId" type="string">
  Шаг, с которого посетитель отправил квиз
</ResponseField>

<ResponseField name="stepName" type="string" />

<ResponseField name="fields" type="Field[]">
  Все ответы
</ResponseField>

<ResponseField name="fieldsSimple" type="object">
  Все ответы в формате «метка → значение»
</ResponseField>

<ResponseField name="response" type="any">
  Ответ сервера LanderLab или `null`, если лид не сохранялся (например, сохранение лидов отключено в настройках квиза)
</ResponseField>

```javascript theme={null}
window.addEventListener('ll-quiz-submit', (event) => {
  fetch('https://example.com/leads', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(event.detail.fieldsSimple),
  });
});
```

## ll-quiz-exit

Срабатывает, когда вкладка или окно закрыты либо посетитель ушёл со страницы; основано на событии браузера `pagehide`.

<Warning>
  Браузеры не гарантируют это событие при каждом закрытии. Используйте его для отслеживания по принципу «по возможности» с помощью `navigator.sendBeacon` и никогда — для действий, которые обязательно должны выполниться.
</Warning>

```javascript theme={null}
window.addEventListener('ll-quiz-exit', (event) => {
  const { llQuizApi } = event.detail;
  if (llQuizApi.getInfo().isSubmitted) return; // finished visits are not abandonments

  const { name } = llQuizApi.getCurrentStep();
  navigator.sendBeacon('https://example.com/abandoned', JSON.stringify({ lastStep: name }));
});
```

## fields и fieldsSimple

`fields` — это список, в котором по одной записи на каждый блок с ответом:

```javascript theme={null}
[
  { id: 'firstName', label: 'First name', value: 'Ada', key: 'firstName', stepId: 'step-…', stepName: 'contact' },
  { id: 'plan', label: 'Plan', value: 'pro', key: 'plan', stepId: 'step-…', stepName: 'plan' },
]
```

`fieldsSimple` — та же информация в виде одного объекта с ключами-метками, удобная для пересылки:

```javascript theme={null}
{ 'First name': 'Ada', 'Plan': 'pro' }
```

Оба варианта не включают скрытые блоки и пустые ответы. Если у двух блоков одинаковая метка, `fieldsSimple` объединяет их значения через запятую. Quiz API возвращает те же структуры из `getAnswers()` и `getAnswersSimple()`.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.