GTM — Countdown Promo Cart
Script que inyecta un countdown regresivo en el bloque de promoción del carrito (.hw-cart_list-item-first-applicable). Mientras la cuenta atrás está activa puede ocultar el mensaje promocional original. Cuando la fecha de fin llega, el countdown desaparece y restaura el estado inicial.
Variables GTM
Los textos y fechas se configuran desde variables del Data Layer en GTM, no en el código:
| Variable GTM | Descripción |
|---|---|
{{countdown-cart-promo-message-intro}} | Texto introductorio antes del countdown (ej. "¡Oferta termina en:"). |
{{countdown-cart-promo-start-date}} | Fecha y hora de inicio — antes de esta fecha el countdown no se muestra. Formato JS Date válido (ej. "2025-07-20T00:00:00"). |
{{countdown-cart-promo-end-date}} | Fecha y hora de fin — cuando se alcanza, el countdown se elimina. |
{{countdown-cart-promo-label-days}} | Etiqueta de días (ej. "días"). |
{{countdown-cart-promo-label-hours}} | Etiqueta de horas (ej. "h"). |
{{countdown-cart-promo-label-minutes}} | Etiqueta de minutos (ej. "min"). |
{{countdown-cart-promo-label-seconds}} | Etiqueta de segundos (ej. "seg"). |
Flags de configuración
Variables booleanas/numéricas que controlan el comportamiento desde el código:
| Variable | Valor por defecto | Descripción |
|---|---|---|
gtmCountdownPromoCartDelay | 1000 | Milisegundos de espera antes de inicializar. 0 = sin espera. |
gtmCountdownPromoCartShowDays | false | Si true, muestra la columna de días y las horas se calculan módulo 24. Si false, las horas acumulan días (pueden superar 24). |
gtmCountdownPromoCartShowIntro | true | Si false, no inyecta el párrafo de texto introductorio. |
gtmCountdownPromoCartHideOriginalMessage | true | Si true, oculta el mensaje original del carrito mientras el countdown está activo. Si false, añade además la clase gtm-html-cart-promo-message al contenedor para mostrar ambos apilados. |
Elementos del DOM
| Selector | Descripción |
|---|---|
.hw-cart_list-item-first-applicable | Contenedor donde se inyecta el countdown. |
.hw-cart_list-item-first-applicable span:nth-child(2) | Mensaje de promoción original (se oculta si hideOriginalMessage es true). |
Estructura HTML generada
<!-- Dentro de .hw-cart_list-item-first-applicable -->
<div class="gtm-js-countdown-content gtm-html-countdown-content">
<!-- Solo si gtmCountdownPromoCartShowIntro = true -->
<p class="gtm-js-countdown-intro gtm-html-countdown-intro">
{{countdown-cart-promo-message-intro}}
</p>
<!-- Countdown (comienza con skeleton, se reemplaza en el primer tick) -->
<div class="gtm-js-countdown gtm-html-countdown">
<div class="gtm-html-countdown-wrapper">
<!-- Días: solo si showDays = true Y days > 0 -->
<div class="gtm-html-countdown-element">
<p class="gtm-html-countdown-number">02</p>
<p class="gtm-html-countdown-label">días</p>
</div>
<p class="gtm-html-countdown-separator">:</p>
<!-- Horas: si hours > 0 O si hay días -->
<div class="gtm-html-countdown-element">
<p class="gtm-html-countdown-number">14</p>
<p class="gtm-html-countdown-label">h</p>
</div>
<p class="gtm-html-countdown-separator">:</p>
<!-- Minutos: siempre -->
<div class="gtm-html-countdown-element">
<p class="gtm-html-countdown-number">07</p>
<p class="gtm-html-countdown-label">min</p>
</div>
<p class="gtm-html-countdown-separator">:</p>
<!-- Segundos: siempre -->
<div class="gtm-html-countdown-element">
<p class="gtm-html-countdown-number">33</p>
<p class="gtm-html-countdown-label">seg</p>
</div>
</div>
</div>
</div>
Separadores
Los <p class="gtm-html-countdown-separator"> tienen display: none por CSS. Se incluyen en el DOM pero no son visibles — están preparados para activarse con una regla CSS si se necesita en el futuro.
Clases CSS
El script separa clases en dos namespaces para evitar colisiones:
- Clases visuales (gtm-html-*)
- Clases de selección (gtm-js-*)
| Clase | Elemento | Descripción |
|---|---|---|
gtm-html-cart-promo-message | Contenedor | Añade flex-wrap: wrap cuando el mensaje original no se oculta. |
gtm-html-countdown-content | div | Fila de countdown: flex, flex-wrap: wrap, gap: 4px 10px. Cuando está dentro de gtm-html-cart-promo-message, ocupa width: 100%. |
gtm-html-countdown-intro | p | Texto intro, color: currentColor. |
gtm-html-countdown-wrapper | div | Fila interna de unidades: flex, gap: 0 4px. |
gtm-html-countdown-element | div | Par número + etiqueta: flex, align-items: baseline, gap: 2px. |
gtm-html-countdown | div | Wrapper del countdown, fuente NimbusSansDOTExtended. |
gtm-html-countdown-number | p | Número: font-size: 20px, font-weight: 700. |
gtm-html-countdown-label | p | Etiqueta: font-size: 8px. |
gtm-html-countdown-separator | p | Separador : — display: none por defecto. |
| Clase | Descripción |
|---|---|
gtm-js-countdown-content | Permite al script recuperar el nodo de contenido tras inyectarlo. |
gtm-js-countdown | Permite al script recuperar el nodo del countdown. |
gtm-js-countdown-intro | Permite al script recuperar el nodo del intro. |
Flujo de ejecución
Espera gtmCountdownPromoCartDelay ms (defecto: 1000ms)
│
└─ fnGtmInitCountdownPromoCart()
│
├─ fnGtmCountdownPromoCartToPreInitElements()
│ ├─ Busca container + originalMessage
│ └─ Si !hideOriginalMessage → añade gtm-html-cart-promo-message al container
│
├─ fnGtmCountdownPromoCartToInjectCode()
│ ├─ Crea div.content + p.intro (si showIntro) + div.countdown (con skeleton)
│ └─ Los añade al container (inicialmente ocultos con is-hidden)
│
├─ fnGtmCountdownPromoCartToInitElements()
│ └─ Recupera los nuevos elementos via gtm-js-* selectors
│
└─ fnGtmCountdownPromoCartToInitCountdown()
├─ Si now < startDate → no hace nada (countdown aún no debe mostrarse)
└─ Si now >= startDate Y diferencia > 0:
└─ fnGtmCountdownPromoCartToShowCountdown()
├─ Oculta originalMessage (si hideOriginalMessage)
├─ Muestra intro + countdown (quita is-hidden)
└─ setInterval cada 1000ms → fnGtmCountdownPromoCartToUpdateCountdown()
├─ Calcula días / horas / minutos / segundos restantes
├─ Construye HTML y actualiza countdown.innerHTML
└─ Si diferencia <= 0:
└─ fnGtmCountdownPromoCartToRemoveCountdown()
├─ Elimina intro + countdown del DOM
├─ Restaura originalMessage
└─ clearInterval
Lógica de horas según showDays
showDays | Cálculo de horas | Efecto |
|---|---|---|
false (defecto) | Math.floor(difference / 3600000) | Las horas son el total acumulado (ej. 50h si quedan 2 días y 2h). |
true | Math.floor((difference % 86400000) / 3600000) | Las horas son módulo 24; los días se muestran en su propia columna. |
Funciones de referencia
| Función | Descripción |
|---|---|
fnGtmCountdownPromoCartToPreInitElements | Localiza el contenedor y el mensaje original. |
fnGtmCountdownPromoCartToInjectCode | Crea y añade la estructura HTML del countdown al DOM. |
fnGtmCountdownPromoCartToInitElements | Recupera los elementos recién creados para manipularlos después. |
fnGtmCountdownPromoCartToInitCountdown | Comprueba fechas y arranca el countdown si procede. |
fnGtmCountdownPromoCartToShowCountdown | Hace visible el countdown e inicia el intervalo de actualización. |
fnGtmCountdownPromoCartToRemoveCountdown | Elimina el countdown del DOM, restaura el mensaje original y detiene el intervalo. |
fnGtmCountdownPromoCartToGetDifference | Devuelve ms restantes hasta endDate. |
fnGtmCountdownPromoCartToUpdateCountdown | Recalcula y reescribe el HTML interno del countdown cada segundo. |
fnGtmCountdownPromoCartToPadZero(n) | Añade 0 inicial a números menores de 10. |