Enhanced Ecommerce Tracking (EEC por sus siglas en inglés) es la solución de Google Analytics para implementar analítica en un ecommerce. Este tipo de ‘sites’ tienen unas características que hacen que sea necesario una implementación de analítica propia y diferenciada: impresiones de productos, impresiones de promociones, productos añadidos al carrito de la compra, transacciones, devoluciones, etc.
Con GA4, Enhanced Ecommerce cobra una nueva dimensión con respecto a Universal Analytics. Su esquema de medición e implementación cambia (aunque los pilares básicos siguen siendo los mismos): los dataLayers que se usan tienen una estructura diferente, se usan eventos propios de GA4, las configuraciones en GTM y GA cambian, etc. Bajo mi punto de vista, Enhanced Ecommerce gana con GA4. Su implementación es más sencilla y flexible.
Hay muchas formas diferentes de implementar EEC de en tu ‘site’. Una de las más óptimas -y extendidas- es implementando unos dataLayers y trabajando con Google Tag Manager (GTM). En este post voy a seguir esta senda y te voy a explicar cómo puedes implementar Enhanced Ecommerce de GA4 en tu ‘site’ generando tus propios dataLayers desde GTM. O por lo menos cómo lo he hecho yo en este blog (sí, puedes implementar EEC en un cualquier tipo de ‘site’, incluso si no es un ecommerce).
Un plan de medición para no perderte en tu implementación
Enhanced Ecommerce es una de las implementaciones más complejas que puedes hacer con Google Analytics (en este caso GA4). Es importante que tengas un plan de medición que te sirva como guía en tu implementación. Hay muchos eventos de EEC con los que puedes trabajar, y quizá no todos se ajusten a tus necesidades. Estúdialos y escoge los que más se adapten a tus necesidades, encontrarás el listado en la documentación oficial de GA4 de eventos recomendados (puedes usar los eventos que quieras, pero Google Analytics te recomienda estos por facilitar integraciones con otros productos).
En mi caso yo he escogido los siguiente eventos para implementar Enhanced Ecommerce de GA4 en mi ‘site’:
- view_item_list → Evento que se ejecuta cuando un usuario visualiza una lista de productos
- select_item → Evento que se ejecuta cuando un usuario hace clic en uno de estos productos
- view_item → Evento que se ejecuta cuando un usuario visualiza la ficha de un producto
- add_to_cart → Evento que se ejecuta cuando un usuario añade un producto al carrito
- purchase → Evento que se ejecuta al comprar un producto
Google Tag Manager trabaja de forma muy estrecha con el dataLayer, y en el caso de EEC de GA4, todos los eventos con los que puedes trabajar van de la mano de un dataLayer propio. No voy a entrar al detalle de cada uno de estos dataLayers (puedes consultarlos en la documentación oficial de Enhanced Ecommerce de Google), pero debes saber que, como te digo, cada uno de estos eventos va necesariamente emparejado de una capa de datos determinada.
Sigue leyendo, te explicaré cuál es la configuración de cada uno de estos dataLayers y cómo generarlos desde GTM.
Generar tus propios dataLayers desde Google Tag manager (GTM)
En realidad no voy a generar mis propios dataLayers desde GTM (Google Tag Manager se encarga de inicializar el dataLayer), sino que más bien actualizaré la información del dataLayer con un dataLayer.push()
diferente desde GTM en diferentes momentos de la navegación del usuario por mi ‘site’.
Ojo, aunque esto es perfectamente posible y técnicamente correcto, en mi opinión es aconsejable que sea un desarrollador el que implemente un dataLayer asociado a la carga de una página (hay un montón de ‘pluggins’ que también puedes usar). Es el caso, por ejemplo, del dataLayer asociado del evento view_item_list, que expone información relativa a los productos que se imprimen en una pantalla. Estos productos pueden cambiar en cualquier momento, por lo que es mucho más estable que un desarrollador se encargue de implementar este dataLayer y de que sus valores se informen de forma dinámica desde el ‘back-end’
Pero imagínate que no tienes soporte de Desarrollo. En un escenario así tienes que empezar a trabajar tú mismo actualizando el dataLayer con llamadas dataLayer.push()
¿Y de dónde vas a sacar los datos que vas a empujar al dataLayer? Del DOM, cosa por otra parte harta desaconsejada en la mayoría de las implementaciones porque tu DOM cambiará a lo largo del tiempo (clases, ID’s, atributos, etc.), y esto provocará que tu implementación se caiga, o por lo menos que tosa. Ten esto en cuenta, es muy importante.
Sea como sea, en la implementación que te muestro a continuación verás un montón de código JavaScript desarrollado para interactuar con el DOM. ¡Aprovéchalo! Saber interactuar con las API’s que tu navegador te ofrece es un valor seguro.
Una última cosa antes de continuar. Para lanzar todos estos dataLayer.push()
voy a trabajar con CHTML Tags. Google Tag Manager te anima a que, en la medida de lo posible, uses custom templates para ejecutar JavaScript. Por si te interesara explorar esta vía, en mi perfil de GitHub encontrarás el repositorio de un custom template que he desarrollado precisamente para ejecutar dataLayer.push()
.
El dataLayer del evento view_item_list
Este dataLayer se debe implementar en las páginas en las que expongas tus listas de productos, de ahí el nombre del evento que se ejecuta con el mismo: view_item_list. Como yo estoy implementado Enhanced Ecommerce en este ‘site’, mis listas de productos están en la página principal o en las secciones, como por ejemplo la de Tealium iQ. Los productos son los extractos de posts que se pueden ver en cada uno de estos listados.
<script>
(function () {
try{
var dataLayer = window.dataLayer || [];
//Leverage the document.querySelectorAll API to generate a node with all post titles exposed on the page
var itemNames = document.querySelectorAll('h4.uael-post__title');
//Leverage the document.querySelectorAll API to generate a node with all categories of all post categories
var itemCategories = document.querySelectorAll('h4.uael-post__title a');
//Create an empty array
var itemsArray = [];
for (var i = 0; i < itemNames.length; i++) {
//Using this 'for' loop to generate an array with i child objects
itemsArray[i] = {
//Generate item_name from itemNames[i]
item_name: itemNames[i].innerText.toLowerCase(),
//Dummy item_id
item_id: '000' + itemNames[i].innerText.toLowerCase(),
//Dummy price
price:10,
//Dynamically generate the value of the item_category key with the url of the itemCategories[i]
item_category:itemCategories[i].href.split('/')[3].replace(/-/gm,' '),
//Cocatenate the value of the dataLayer varible DLV - page_type and the string 'list'
item_list_name: {{DLV - page_type}} + ' list' ,
//Index of item in the item list
index: i + 1,
quantity:1}
}
//dataLayer.push() containing the 'view_item_list' event and the ecommerce array
dataLayer.push({
event: "view_item_list",
ecommerce:{items:itemsArray}
})
}
catch(e){}
})();
</script>
En esencia lo que estoy haciendo es usar la document.querySelectorAll()
browser API para guardar en una variable un nodo con los nombres de los artículos que se ven en cada listado, y en otra un nodo con su categoría. Después inicializo un array vacío que lleno con tantos objetos como ‘posts’ se muestren en mi listado de productos. Para esto último uso el ‘for loop’, que además se encarga de configurar cada objeto con la información relativa a cada uno de estos posts.
Importante: los selectores que uso en las dos llamadas document.querySelectorAll()
se ajustan al DOM de este site. Tú tendrás que usar unos selectores que se ajusten al tuyo.
También importante: no estoy usando todas las variables que presenta Google para este dataLayer en la documentación oficial. Uso las que para mi site tienen sentido, entre las cuáles está una que sí es obligatoria: item_name
Como te digo, para ejecutar el anterior código puedes usar un CHTML Tag, pero no debes permitir que se ejecute antes que el tag de configuración de GA4, de lo contrario podrías incurrir en un ‘race condition’. Para evitar esto, ejecuto mediante secuenciación de etiquetas un dataLayer.push()
justo después del tag de configuración de GA4, así tengo constancia de que ya se ha terminado de ejecutar. Fíjate:
Al final de este post encontrarás la secuencia completa de acontecimientos en GTM .
El dataLayer del evento select_item
El evento select_item se debe ejecutar cuando un usuario hace clic en uno de los productos que ha visto en un listado (en mi caso en uno de los ‘posts’) Esto implica que el dataLayer de este evento no está disponible desde el principio (al contrario de lo que sucede con el dataLayer del evento view_item_list, que está disponible desde la carga de la página). Así las cosas, hay que ejecutar un dataLayer.push()
cuando el usuario haga clic en uno de estos productos, y la llamada debe incorporar información relativa al producto sobre el cual se ha hecho clic.
Para ejecutar un dataLayer.push()
al hacer clic en un producto podrías desarrollar todo el código en un CHTML Tag, pero he preferido optar por otra la senda de los ‘utility functions‘, que no son otra cosa que la forma de aplicar en GTM las bondades de los ‘closures’ en JavaScript (cielos, cómo suena eso). En esencia lo que hago es usar un CHTML Tag para pasar a una función que tengo creada en un Custom Javascript Variable el argumento ‘Clic Element’ (esta variable nativa de GTM alojará el elemento del DOM sobre el que se haga clic, en este caso el extracto de un post). Hecho esto, la ‘utility function’ que contiene el Custom Javascript Variable se encargará de ejecutar dataLayer.push()
.
Update: si quieres saber más sobre los Javascript closures en GTM, te recomiendo que leas mi post ‘Cómo aprovechar los Javascript closures en Google Tag Manager (GTM)
El código de este CHTML Tag es muy sencillo:
<script>
(function () {
try{
//Passing the utility function contained in the Custom Javascript Variable an argument: Click Element variable
{{CJS - enhanced ecommerce - select_item}}({{Click Element}})
}
catch(e){}
})();
</script>
Y visto en su CHTML Tag:
En cuanto al código del ‘utility function’ desarrollado en el ‘Custom Javascript Variable’, es el siguiente:
function(){
// Passing the clickedElement argument to the utility function
return function(clickedElement){
try{
var dataLayer = window.dataLayer || [];
//item_name
var itemName = clickedElement.parentElement.querySelector('h4').innerText.toLowerCase();
//item_id -- Dummy ID
var itemId = '000' + itemName;
//item_category -- Obtained from the URL of the clicked item
var itemCategory = clickedElement.href.split('/')[3].replace(/-/gm,' ');
//item_list_name
var itemListName = {{DLV - page_type}} + ' list';
//index -- Position of the clicked in time with regards to the rest of items
var index = function(){
var postListWrapper = clickedElement.closest('div.uael-post__body').querySelectorAll('div.uael-post-wrapper div.uael-post__bg-wrap');
for(var i = 0; i < postListWrapper.length; i++){
if(postListWrapper[i].querySelector('a').href === clickedElement.href){
return i + 1
break;
}
}
};
//quantity
var quantity = 1;
//price
var price = 10;
dataLayer.push({ ecommerce: null });
dataLayer.push({
event:'select_item',
ecommerce:{
items:[
{
item_name:itemName,
item_category:itemCategory,
item_id:itemId,
item_list_name:itemListName,
index:index(),
quantity:quantity,
price:price
}
]
}
})
}
catch(error){
}
}
}
Y aquí lo tienes visto en Google Tag Manager, dentro de la variable de tipo JavaScript personalizado (fíjate en cómo la variable referencia al CHTML Tag en la que se usa):
dataLayer.push()
con ellos. El dataLayer del evento view_item
El evento view_item se ejecuta al visualizar un producto de tu ecommerce (en mi caso al visualizar un post). Su dataLayer debe estar presente en el momento de la carga de esta página. En este sentido, su implementación es similar a la del evento view_item
_list: debes generar este dataLayer con un código desarrollado en un CHTML tag que se ejecutará después del tag de configuración de GA4 para evitar un race condition. La única diferencia es que este dataLayer.push()
debe ejecutarse sólo en los ‘posts’, y no en los listados de productos. Esto se consigue a través de una variable que expone el propio dataLayer implementado en este site. Puedes verlo en los pantallazos que incluyo más abajo.
Este el código que yo he usado para generar el dataLayer view_item en los posts de mi blog, fíjate:
<script>
(function () {
try{
var dataLayer = window.dataLayer || [];
//Ternary operator that uses the document.referrer API to see if previousPage was homepage or category section
var rutaAnterior = document.referrer.split('/').length > 4 ? 'seccion list' : 'home list';
var hostString = 'https://analyticsimplementations.com';
//If document referrer includes analyticsimplementations.com dataLayer.push() includes item_list_name
if(document.referrer.includes(hostString)){
dataLayer.push({
event: "view_item",
ecommerce: {
items: [{
item_name: {{CJS - h1 inner text lower case}},
//Dummy id
item_id: '000' + {{CJS - h1 inner text lower case}},
price: 10,
item_category: {{DLV - page_category}},
item_list_name: rutaAnterior,
index: 1,
quantity: 1
}]
}
});
//If document referrer doesn't include analyticsimplementations.com, dataLayer.push() doesn't include item_list_name
} else {
dataLayer.push({
event: "view_item",
ecommerce: {
items: [{
item_name: {{CJS - h1 inner text lower case}},
//Dummy Id
item_id: '000' + {{CJS - h1 inner text lower case}},
price: 10,
item_category: {{DLV - page_category}},
index: 1,
quantity: 1
}]
}
});
}
} catch(e){}
})();
</script>
Y como te decía un poco más arriba, la secuencia de acontecimientos es la misma que en el caso del evento view_item_list:
El dataLayer del evento add_to_cart
El evento add_to_cart se ejecuta cuando un usuario añade un producto de tu ecommerce a su cesta de la compra. En ese instante se debe ejecutar un dataLayer.push()
con la información de ese ítem, y este .push() será el que accione el evento. Como este ‘site’ no es un ecommerce, he tenido que customizar un poco el evento add_to_cart. He considerado que este evento se debe accionar en el momento en que un usuario llega al 50% de la longitud total de un artículo, es decir, lee por lo menos hasta la mitad del contenido. Llegado ese momento se ejecuta el siguiente dataLayer.push()
desde un CHTML Tag:
<script>
(function () {
try{
var dataLayer = window.dataLayer || [];
//Ternary operator that uses the document.referrer API to see if previousPage was homepage or category section
var rutaAnterior = document.referrer.split('/').length > 4 ? 'seccion list' : 'home list';
var hostString = 'https://analyticsimplementations.com';
dataLayer.push({ ecommerce: null });
//If document referrer includes analyticsimplementations.com dataLayer.push() includes item_list_name
if(document.referrer.includes(hostString)){
dataLayer.push({
event: 'add_to_cart',
ecommerce: {
items: [{
item_name: {{CJS - h1 inner text lower case}},
//Dummy Id
item_id: '000' + {{CJS - h1 inner text lower case}},
price: 10,
item_category: {{DLV - page_category}},
item_list_name: rutaAnterior,
index: 1,
quantity: 1
}]
}
});
}
//If document referrer doesn't include analyticsimplementations.com dataLayer.push() doesn't item_list_name
else{
dataLayer.push({
event: 'add_to_cart',
ecommerce: {
items: [{
item_name: {{CJS - h1 inner text lower case}},
//Dummy Id
item_id: '000' + {{CJS - h1 inner text lower case}},
price: 10,
item_category: {{DLV - page_category}},
index: 1,
quantity: 1
}]
}
});
}
} catch(e){}
})();
</script>
Como ves, es exactamente el mismo código que se ejecuta en el evento view_item. Tiene sentido, al fin y al cabo estás añadiendo al carrito el mismo producto que estás viendo en ese instante. Siendo así, este código se podría optimizar un montón para re-aprovechar el contenido del dataLayer y sólo actualizar el parámetro ‘event’.
Un único tag para enviar todos estos eventos a GA4
Ya sólo queda un paso más para completar la implementación: configurar el evento purchase. Pero antes quiero mostrarte cómo puedes enviar los eventos descritos hasta ahora a GA4. De nada te servirá configurar y ejecutar todos estos dataLayers si no eres capaz de trasladar los eventos a Google Analytics 4.
Yo creo que lo más óptimo es configurar un único tag de evento de GA4 que se ejecute con todos los anteriores eventos. De esta forma optimizas y simplificas un montón tu implementación y tu contenedor de GTM. El ‘payload’ de este tag siempre será el mismo: la variable ‘items’ del dataLayer. Siendo así, lo único que tienes que hacer es configurar un trigger que responda a todos estos eventos.
El paso final: el dataLayer del evento purchase
El evento purchase es el más importante de todos. Al fin y al cabo es el que te va a servir para medir las compras en tu ecommerce. En mi caso lo he tenido que customizar, al igual que el evento add_to_cart. El evento purchase se ejecuta en este site en el momento en que un usuario llega al final de un artículo, es decir, cuando lo lee por completo.
Siendo así, cuando un usuario lee el 100% de un post (es decir, cuando tramita una compra) se ejecuta el siguiente código desde un CHTML Tag:
<script>
(function () {
try{
var dataLayer = window.dataLayer || [];
//Generate random no. and convert it to string to use as transaction_id
var idTransaccion = {{Random Number}}.toString()
//Ternary operator that uses the document.referrer API to see if previousPage was homepage or category section
var rutaAnterior = document.referrer.split('/').length > 4 ? 'seccion list' : 'home list';
var hostString = 'https://analyticsimplementations.com';
dataLayer.push({ ecommerce: null });
//If document referrer includes analyticsimplementations.com dataLayer.push() includes item_list_name
if(document.referrer.includes(hostString)){
dataLayer.push({
event: "purchase",
ecommerce: {
transaction_id: idTransaccion,
//Dummy affiliation
affiliation: "Dummy affiliation",
//Dummy value
value: "13.8",
//Dummy tax value
tax: "1.90",
//Dummy shipping value
shipping: "1.90",
currency: "EUR",
coupon: "NO_COUPON",
items: [{
item_name: {{CJS - h1 inner text lower case}},
//Dummy Id
item_id: '000' + {{CJS - h1 inner text lower case}},
//Dummy price
price: 10,
item_category: {{DLV - page_category}},
item_list_name: rutaAnterior,
quantity: 1
}]
}
});
}
//If document referrer doesn't include analyticsimplementations.com dataLayer.push() doesn't include item_list_name
else{
dataLayer.push({
event: "purchase",
ecommerce: {
transaction_id: idTransaccion,
//Dummy affiliation
affiliation: "Dummy affiliation",
//Dummy value
value: "13.8",
//Dummy tax value
tax: "1.90",
//Dummy shipping value
shipping: "1.90",
currency: "EUR",
coupon: "NO_COUPON",
items: [{
item_name: {{CJS - h1 inner text lower case}},
//Dummy Id
item_id: '000' + {{CJS - h1 inner text lower case}},
//Dummy price
price: 10,
item_category: {{DLV - page_category}},
quantity: 1
}]
}
});
}
}
catch(e){}
})();
</script>
Como ves, el dataLayer.push()
que ejecuta este código tiene muchos más parámetros que los anteriores eventos: transaction_id, affiliation, shipping, etc. Como te digo un poco más arriba, el evento purchase es el más importante de tu implementación de Enhanced Ecommerce, y como tal tiene más parámetros que trabajar y explotar. Siendo así, el tag de G4 que responde a este evento tiene una configuración diferente:
La implementación de Enhanced Ecommerce (GA4) de principio a fin
Hasta ahora te he mostrado, evento a evento, cómo he generado mis propios dataLayers de Enhanced Ecommerce de Google Analytics 4 (GA4) desde Google Tag Manager (GTM) en mi site. Ahora te voy a mostrar, paso a paso, la implementación de principio a fin.
Primer paso. Un usuario aterriza en una sección de este site (BigQuery, por ejemplo). Se ejecuta el tag de configuración de GA4 y después el CHTML Tag que ejecuta un dataLayer.push()
con el evento page_view_fired notificando de la ejecución de de este tag base de GA4.
dataLayer.push()
con el dataLayer del evento view_item_list. A continuación se ejecuta el tag de evento de GA4 que envía el evento view_item_list a Google Analytics 4. dataLayer.push()
con el evento select_item. Cuarto paso. Al aterrizar en un post, se ejecuta la misma lógica que con el evento view_item_list, solo que en este caso aplicada al evento view_item:
Quinto paso. El usuario hace scroll hasta la mitad de la pantalla del post. Se ejecuta la lógica del evento add_to_cart.
Sexto y último paso. El usuario hace scroll hasta el final del post y se ejecuta el evento purchase.