Ir al contenido

Qué hacer con cada evento

El configurador cuenta lo que hace el comprador mediante eventos del navegador. No hay que activar nada: se emiten siempre, y si nadie escucha no pasa nada. Esta página va evento por evento con ideas concretas; el contrato técnico (campos comunes, versionado, lo que nunca llevan) está en Integración con el tema.

Se emiten sobre <optical-form-drawer> y burbujean, así que basta con escuchar en document. Todos llevan detail con el mismo contexto: sessionId (cose una visita entera), productId, variantId, stepIndex, stepId, stepType y totalSteps.

document.addEventListener('opticalform:open', (e) => {
console.log('configurador abierto en el producto', e.detail.productId);
});

Dos límites que conviene tener claros antes de construir: los eventos no llevan las respuestas del comprador (ni graduación, ni fichero, ni textos) y no se pueden cancelar. Dicen dónde está, no qué ha contestado, y no dejan intervenir.

opticalform:open — se abre el configurador

Sección titulada «opticalform:open — se abre el configurador»

Es el primer momento en que sabes que esa persona quiere montar unas gafas, no solo mirar la montura.

  • Cargar el chat solo ahora. Si tu chat se carga en diferido por privacidad, este evento es el buen momento: quien mira sin tocar no deja cookies de terceros.
  • Marcar la sesión en el chat o en la analítica: «está configurando gafas» vale más que «vio un producto».
  • Arrancar un reloj: el tiempo desde aquí hasta :addtocart:success es tu tiempo de configuración real.
document.addEventListener('opticalform:open', () => {
if (!window.miChatCargado) cargarMiChat(); // tu función, tu proveedor
});

opticalform:step:rendered — el comprador llega a un paso

Sección titulada «opticalform:step:rendered — el comprador llega a un paso»

Trae además isFirst e isLast. Se emite cuando el paso cambia, no en cada repintado.

  • Embudo por paso en tu analítica: sabrás en qué paso se queda la gente, que es la pregunta que decide qué arreglar.
  • Mensaje contextual del chat al llegar a la graduación (stepType graduation, reading_rx o contact_lens): «¿tienes tu receta a mano? Si no, sube una foto». Una vez por sesión, no en cada paso.
  • Preparar el final cuando isLast es true: por ejemplo, avisar de que el envío es gratis.
document.addEventListener('opticalform:step:rendered', (e) => {
dataLayer.push({ event: 'lens_step', step: e.detail.stepType, index: e.detail.stepIndex });
});

opticalform:validation:error — un paso no le deja avanzar

Sección titulada «opticalform:validation:error — un paso no le deja avanzar»

No lleva el valor que falló, solo el paso.

  • Contar en qué paso tropieza la gente. Si un paso concentra los errores, el problema es el paso: su texto de ayuda, su ejemplo, su obligatoriedad.
  • Ofrecer ayuda tras varios intentos: al segundo o tercer error en el mismo paso, el chat puede abrirse con una pregunta directa.

opticalform:addtocart:start — empieza el añadido

Sección titulada «opticalform:addtocart:start — empieza el añadido»

Entre este evento y el siguiente el configurador está hablando con el carrito de Shopify.

  • Esconder lo que estorbe durante el añadido, por ejemplo un lanzador de chat que flota sobre el botón.
  • Medir la latencia hasta :addtocart:success: si sube, algo ha cambiado en el tema o en una app del carrito.

opticalform:addtocart:success — el carrito lo aceptó

Sección titulada «opticalform:addtocart:success — el carrito lo aceptó»

Trae lineCount, totalPrice (en céntimos) y currency. Es tu evento de conversión.

  • Enviar la conversión a GA4, Meta o TikTok con su valor. Antes de este momento el precio no viaja a propósito; después ya está en /cart.js.
  • Recomendar el complemento que no eligió: un estuche, un segundo par, un tratamiento.
  • Explicar el carrito a quien lo ve por primera vez: la montura y cada lente o tratamiento llegan como líneas separadas, con su precio. Es lo que recibe tu laboratorio.
document.addEventListener('opticalform:addtocart:success', (e) => {
gtag('event', 'add_to_cart', {
value: e.detail.totalPrice / 100,
currency: e.detail.currency,
items: [{ item_id: String(e.detail.productId), quantity: e.detail.lineCount }],
});
});

Trae kind — la causa — y line — qué línea falló. kind es uno de sold_out (la montura está agotada), addon_unavailable (un tratamiento, color o accesorio no se puede vender), limit (el carrito ya tiene todo el stock), unavailable (algo ya no existe), throttled (Shopify pidió esperar) o unknown; line es frame, addon o unknown. Sale también cuando el panel se niega a enviar sin llamar a Shopify, porque ya sabía que la montura o un complemento estaban agotados.

  • Avisar a soporte con sessionId, productId, kind y line: llegas antes de que el comprador escriba.
  • Reintentar solo si es throttled; los demás no se arreglan repitiendo.
  • Vigilar addon_unavailable: cada uno es una variante de lente, color o accesorio que no se puede vender mientras la gente la elegía — el editor te lo enseña con Comprobar precios. sold_out es la montura: el botón de la ficha ya debería decir «Agotado».

Trae completed: true si terminó, false si se fue. Es toda la diferencia para recuperar un abandono.

  • Abandono con contexto: con completed a false sabes en qué paso estaba (stepType, stepIndex) y en qué producto. Un mensaje del chat, un aviso de «te guardamos la configuración» o un formulario de correo valen más aquí que en cualquier popup de salida.
  • No molestar a quien terminó: con completed a true lo único que toca es dejarle ir al carrito.
document.addEventListener('opticalform:close', (e) => {
if (!e.detail.completed) miChat.mensaje('¿Te has quedado con dudas? Escríbenos aquí.');
});

opticalform:resume — el comprador retoma una configuración a medias

Sección titulada «opticalform:resume — el comprador retoma una configuración a medias»

Si cerró el configurador sin terminar y vuelve al mismo producto en 7 días, la app le ofrece «Continuar donde lo dejaste». Este evento sale cuando lo pulsa. Trae restoredSteps: cuántos pasos han recuperado su respuesta. Como todos, no dice qué respondió. Y solo sale si el comerciante ha activado «Recordar la configuración a medias» en Diseño › Navegación (viene apagado de fábrica).

  • Medir si sirve: cuántos resume frente a cuántos close sin completed dice si vale la pena mantenerlo encendido. La pestaña Analítica ya te lo cuenta («N retomaron una configuración a medias»).
  • Cambiar el tono del chat: quien retoma ya decidió la mitad; un «¿te ayudo con la graduación?» encaja mejor que un «¿empezamos?».
document.addEventListener('opticalform:resume', (e) => {
miAnalitica.evento('configurador_retomado', { pasos: e.detail.restoredSteps });
});

optical-form:cart:orphan — en el carrito falta una línea hija

Sección titulada «optical-form:cart:orphan — en el carrito falta una línea hija»

Este no lo emite el configurador sino el puente del carrito, en document, cuando detecta una montura sin las lentes o tratamientos con los que salió: el cliente las borró desde el carrito del tema. Trae groupId, key, expected y actual. La app pinta un aviso traducido bajo la fila y no borra nada ni bloquea el checkout.

  • Decidir tú: quitar la montura, avisar por chat, o dejarlo pasar si en tu tienda una montura se puede comprar sola. Es decisión del comerciante, no un fallo.
  • Medirlo: cuántos borran las lentes dice si el precio del complemento sorprende en el carrito.

Embudo en Google Analytics 4. Escucha :open, :step:rendered, :validation:error, :addtocart:success y :close, y manda cada uno como evento con stepType e stepIndex. En GA4, un informe de embudo por stepIndex te enseña la caída paso a paso; sessionId como dimensión personalizada une la visita entera.

Chat que acompaña. Carga tu proveedor en :open, guarda en sessionStorage qué mensajes ya has enseñado para no repetirlos, y lanza como mucho uno por hito: llegar a la graduación, añadir al carrito, cerrar sin terminar. Si tu chat vive abajo a la derecha, dale sitio con el ajuste Espacio libre a la derecha del pie (Diseño › Navegación) para que no tape el botón de continuar: ver Diseño y carrito.

Recuperación de abandono. En :close con completed a false, guarda productId y stepIndex; si tienes el correo del cliente (porque ha iniciado sesión o lo pide tu popup), tu herramienta de correo puede enviarle un «sigue donde lo dejaste» con el enlace al producto. La app no guarda la configuración a medias: lo que recuperas es la intención, no las respuestas.

  • Cancelar un paso, un añadido o un cierre: preventDefault() no hace nada, a propósito.
  • Leer la graduación, el fichero subido o los textos del comerciante: el detail es una lista cerrada. stepType sí viaja, y es un dato de salud inferido; trátalo como tal.
  • Cambiar lo que valida un paso: eso se configura en el formulario, no en un script.

Los detalles de versionado y repintados están en Integración con el tema.