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

# Usa la API de eventos del quiz para seguimiento y automatización

> Reacciona a lo que ocurre en tu quiz: se carga, se muestra un paso, cambia una respuesta, falla una validación o el visitante lo envía.

Tu quiz anuncia lo que ocurre mediante eventos del navegador en `window`. Escúchalos para medir el comportamiento en tu herramienta de analítica, reaccionar a las respuestas o ejecutar tu propio código en el momento adecuado.

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

Cada evento guarda sus datos en `event.detail`. Siempre hay dos cosas:

* `quizId`: qué quiz ha disparado el evento
* `llQuizApi`: la [Quiz API](/es/features/quizzes/api/reference), para que puedas leer o modificar el quiz directamente en el listener

<Note>
  `ll-quiz-init` se dispara en cuanto el quiz está listo, lo que puede ocurrir antes de que se ejecute tu script. No te preocupes: un listener registrado tarde también recibe `ll-quiz-init`. Todos los demás eventos se disparan con acciones del visitante, cuando tu script ya está cargado.
</Note>

## Eventos de un vistazo

| Evento                          | Se dispara cuando                    | Datos útiles                                    |
| :------------------------------ | :----------------------------------- | :---------------------------------------------- |
| `ll-quiz-init`                  | El quiz está listo                   | `llQuizApi`                                     |
| `ll-quiz-step-view`             | Se muestra un paso                   | `stepName`, `fields`                            |
| `ll-quiz-step-leave`            | El visitante sale de un paso         | `stepName`                                      |
| `ll-quiz-value-change`          | Se confirma una respuesta            | `blockName`, `value`, `previousValue`, `source` |
| `ll-quiz-input-click`           | Se hace clic en una opción           | `blockName`, `optionLabel`, `optionValue`       |
| `ll-quiz-button-click`          | Se hace clic en un botón             | `blockName`                                     |
| `ll-quiz-validation-error`      | Se ha bloqueado la salida de un paso | `errors`, `source`                              |
| `ll-quiz-google-address-select` | Se elige una dirección               | `address`                                       |
| `ll-quiz-submit`                | Se ha enviado el quiz                | `fields`, `fieldsSimple`, `response`            |
| `ll-quiz-exit`                  | Se cierra la pestaña                 |                                                 |

## ll-quiz-init

Se dispara una vez, cuando el quiz está en la página y listo. Aquí es donde obtienes `llQuizApi`, rellenas valores o preparas lo que necesites.

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

<ResponseField name="llQuizApi" type="LlQuizApi">
  La Quiz API de este quiz
</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

Se dispara cada vez que se muestra un paso, incluido el primero al cargar y cuando el visitante vuelve atrás.

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

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

<ResponseField name="fields" type="Field[]">
  Respuestas dadas hasta el momento, consulta [fields y fieldsSimple](#fields-and-fieldssimple)
</ResponseField>

<ResponseField name="fieldsSimple" type="object">
  Las mismas respuestas como etiqueta → valor
</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

Se dispara cuando el visitante sale de un paso, antes del siguiente `ll-quiz-step-view`. Úsalo para medir cuánto tiempo ha tardado en un paso o para guardar un borrador.

<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

Se dispara cuando se confirma una respuesta. Los campos de escritura (texto, email, teléfono, número, área de texto, fecha, fecha de nacimiento, código postal, dirección, OTP) se confirman cuando el visitante sale del campo o del paso, así que recibes un evento por respuesta, no uno por cada tecla. Cualquier otro campo, y cualquier valor establecido mediante la API, se confirma al instante.

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

<ResponseField name="blockName" type="string">
  El nombre del bloque en el editor
</ResponseField>

<ResponseField name="blockType" type="string">
  Por ejemplo `text-field` o `multiple-choice`
</ResponseField>

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

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

<ResponseField name="value" type="string">
  La nueva respuesta, en el mismo formato que `Field.value`
</ResponseField>

<ResponseField name="previousValue" type="string">
  La respuesta notificada la última vez, o el valor con el que se cargó el quiz
</ResponseField>

<ResponseField name="source" type="'user' | 'api' | 'system'">
  `user`: el visitante. `api`: tu script mediante la Quiz API. `system`: el propio quiz, por ejemplo al borrar las respuestas después del envío
</ResponseField>

No se dispara nada si el valor no ha cambiado. Los valores que ya existen cuando se carga el quiz (valores por defecto, parámetros de URL, respuestas restauradas) son el punto de partida y no disparan el evento; están en `fields` en `ll-quiz-step-view`.

```javascript theme={null}
window.addEventListener('ll-quiz-value-change', (event) => {
  const { blockName, value, source } = event.detail;
  if (source !== 'user') return; // ignora los cambios hechos por script y los automáticos

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

## ll-quiz-input-click

Se dispara cuando el visitante hace clic en una opción de un bloque de opción múltiple o de elección con imágenes. Se dispara antes que `ll-quiz-value-change` para el mismo clic.

<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">
  El valor de la opción, tal como se guarda en la respuesta
</ResponseField>

<ResponseField name="optionLabel" type="string">
  El texto que vio el visitante
</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 });

  // Muestra una pista bajo el selector de plan para una opción concreta
  if (blockName === 'plan') {
    const hint = llQuizApi.getBlock('planHint');
    optionValue === 'enterprise' ? hint.visibility.show() : hint.visibility.hide();
  }
});
```

## ll-quiz-button-click

Se dispara cuando se hace clic en un bloque Continue, Previous, Submit o Button, antes de cualquier validación. Se dispara incluso cuando el clic acaba bloqueado, así que cuenta intentos, no éxitos.

<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>
  Para saber si el clic hizo avanzar realmente al visitante, combínalo con `ll-quiz-step-leave` (sí avanzó) o con `ll-quiz-validation-error` (no avanzó).
</Tip>

## ll-quiz-validation-error

Se dispara cuando se bloquea un intento de salir de un paso porque algo en él no es válido. Recibes un evento por intento, cuando han terminado todas las comprobaciones (incluidas las del servidor para los campos de email, teléfono y OTP), con exactamente los errores que ve el visitante. Los bloques ocultos nunca se incluyen.

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

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

<ResponseField name="source" type="'user' | 'api' | 'system'">
  `user`: un botón o una opción que navega. `api`: tu script (`validateStep()`, `goNext()`, `goToStep()`, `submit()`). `system`: el quiz intentó avanzar por sí solo (loader, cuenta atrás, pago)
</ResponseField>

<ResponseField name="triggerBlockId" type="string">
  El botón o la opción que lo inició; vacío para `api`
</ResponseField>

<ResponseField name="triggerBlockName" type="string">
  Vacío para `api`
</ResponseField>

<ResponseField name="errors" type="array">
  Una entrada por cada bloque que falla, en el orden del paso: `blockId`, `blockName`, `blockType` y `messages` (los textos que se muestran; se enseña el primero)
</ResponseField>

No se dispara cuando el paso es válido, cuando se usa el botón Previous (nunca valida) ni cuando se hace clic en una opción que no navega.

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

  // Descubre qué campos frenan a los visitantes
  window.dataLayer?.push({ event: 'quiz_blocked', step: stepName, fields: errors.map((e) => e.blockName) });

  // Y ayúdales: mueve el cursor al primero
  llQuizApi.getBlock(errors[0].blockId).focus();
});
```

## ll-quiz-google-address-select

Se dispara cuando el visitante elige una sugerencia en un bloque 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

Se dispara cuando se ha enviado el quiz, después de guardar el lead. Es el lugar para enviar las respuestas a otro sitio o disparar una conversión.

<ResponseField name="stepId" type="string">
  El paso desde el que el visitante hizo el envío
</ResponseField>

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

<ResponseField name="fields" type="Field[]">
  Todas las respuestas
</ResponseField>

<ResponseField name="fieldsSimple" type="object">
  Todas las respuestas como etiqueta → valor
</ResponseField>

<ResponseField name="response" type="any">
  Lo que respondió el servidor de LanderLab, o `null` si no se guardó ningún lead (por ejemplo, si el guardado de leads está desactivado en los ajustes del quiz)
</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

Se dispara cuando se cierra la pestaña o la ventana, o cuando se navega fuera de ella, a partir del evento `pagehide` del navegador.

<Warning>
  Los navegadores no garantizan este evento en todos los cierres. Úsalo para un seguimiento aproximado con `navigator.sendBeacon`, nunca para algo que tenga que ocurrir sí o sí.
</Warning>

```javascript theme={null}
window.addEventListener('ll-quiz-exit', (event) => {
  const { llQuizApi } = event.detail;
  if (llQuizApi.getInfo().isSubmitted) return; // las visitas completadas no son abandonos

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

## fields and fieldsSimple

`fields` es una lista con una entrada por cada bloque respondido:

```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` es la misma información en un único objeto cuyas claves son las etiquetas, muy práctico para reenviarla:

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

Ambos excluyen los bloques ocultos y las respuestas vacías. Si dos bloques comparten etiqueta, `fieldsSimple` une sus valores con una coma. La Quiz API devuelve las mismas estructuras desde `getAnswers()` y `getAnswersSimple()`.
