Seguimiento de eventos AJAX con Google Tag Manager
- Procesador de eventos AJAX para Google Tag Manager (con jQuery)
- Resultado y aplicaciones
- Caso práctico: Medir llamadas AJAX como eventos de Google Analytics
- Caso práctico: Seguimiento de apertura de ventanas modales o popups
- Caso práctico: Páginas de confirmación virtuales
- Procesador de eventos AJAX para Google Tag Manager (SIN jQuery)
- Bonus track: Captura de datos adicionales

En ocasiones nos encontramos con la necesidad de medir contenido cargado dinámicamente en un sitio web, o acciones relacionadas con la carga del mismo.
Habitualmente, estos tipos de contenido se sirven haciendo uso de tecnología AJAX. Sin ponernos excesivamente teóricos, es una funcionalidad presente en cualquier navegador actual que, mediante Javascript, nos permite hacer peticiones al servidor web (o a otros externos), recibir y procesar las respuestas a estas peticiones y usarlas para crear o actualizar contenido en tiempo real, todo ello sin necesidad de nuevas cargas de página. Algunos ejemplos típicos donde lo encontramos son:
- Páginas con “scroll infinito” o paginación automática sin recarga de página
- Fotos ampliadas de galería
- Diapositivas, elementos de carrusel o banners actualizados dinámicamente
- Ventanas modales, popups de información, mensajes de confirmación/error… en general, cajas de contenido que cargan al realizar determinadas acciones
- Datos que se calculan o actualizan en tiempo real sin recargar la página (ej. carrito de compra, buscador con autocompletar…)
- Widgets de datos obtenidos externamente (ej. tiempo, estadísticas…)
- Aplicaciones web interactivas que requieren peticiones al servidor (ej. chat online, encuestas…)
El problema con que nos encontramos habitualmente es que estos contenidos son dificiles de medir: No estan presentes al cargar la página (no aparecen en el “ver código fuente” de la misma), ni podemos tampoco detectar de forma fácil los momentos en los que cargan para saber cuándo actualizar nuestra información y obrar en consecuencia… Al menos, no utilizando los eventos nativos de Google Tag Manager (clicks, envios de formulario, cambios de historial…)
Detección de eventos AJAX con Google Tag Manager (con jQuery)

“eventos Francis”.
Para poder medirlos, crearemos una Etiqueta de Google Tag Manager que ejercerá como Procesador de eventos: Esto es, estará presente en la página actual, detectando las acciones correspondientes y generando para cada una un evento personalizado de GTM que a su vez podremos utilizar como activadores para otras etiquetas.
1. Requisitos y restricciones
Para conseguirlo, nos valdremos de los controladores de eventos que incluye la librería jQuery con sus funciones AJAX. Esto significa que tendremos dos restricciones:
- jQuery (cualquier versión valdrá) debe estar instalado e incluido en todas las páginas a medir.
- Sólo será compatible con llamadas AJAX realizadas a través de jQuery ($.ajax, .load() y similares). Las peticiones AJAX realizadas via Javascript puro u otros métodos no serán detectadas.
Si estos requisitos son un problema… hay una solución alternativa más abajo 🙂 Pero de momento, sigue leyendo!
2. Variables auxiliares
Además de detectar las llamadas AJAX, el procesador de eventos devolverá datos adicionales asociados a las mismas y a su respuesta, que podremos utilizar para distinguir unas de otras, enviar información extra, etc; para ello, utilizaremos variables de dataLayer genéricas asociadas al evento personalizado y enviadas en el mismo push: eventCategory, eventAction…
Por tanto, para recoger estos datos y poder utilizarlos en etiquetas y activadores, lo primero será crear las Variables de Google Tag Manager asociadas, de tipo Variable de capa de datos:
Dado que utilizan una nomenclatura bastante estandarizada, es muy probable que las tengas creadas ya 😉
3. Etiqueta procesador de eventos
Ahora sí, creamos una etiqueta nueva, de tipo HTML Personalizado, y en ella pegamos el siguiente código:
<script>
(function($) {
$(document).ajaxSuccess(function( event, xhr, settings ) {
dataLayer.push({
'event': 'ajaxSuccess',
'eventCategory': 'AJAX',
'eventAction': settings.url,
'eventLabel': xhr.responseText
});
});
})(jQuery);
</script>
Como activador, lo pondremos en cada carga de página. Aunque en la mayoría de casos funcionará correctamente con el activador por defecto “Todas las páginas”, por precaución (posibles problemas con jQuery no cargado aún, etc) lo retrasaremos hasta después de tener la estructura de la misma cargada, esto es, el evento DOM Preparado (DOM Ready o gtm.dom). Para ello, creamos el activador correspondiente…
…Y lo dejamos seleccionado para la etiqueta que estamos creando. Quedaría así:
Por supuesto, si las llamadas AJAX a medir ocurren únicamente en determinadas páginas o secciones de la web, podemos aprovechar para introducir condiciones adicionales (usando variables tipo {{Page URL}} o {{Page Path}}) para restringir la activación a esas zonas y optimizar rendimiento.
Resultado y aplicaciones
Una vez guardada y publicada la etiqueta, en nuestro contenedor de GTM veremos a partir de ahora -podremos comprobarlo en la ventana de vista previa y depuración- un nuevo evento personalizado llamado ajaxSuccess, con los siguientes datos (variables de dataLayer) asociados:
- event: ajaxSuccess
- eventCategory: AJAX
- eventAction: El URL al que se ha enviado la petición. Normalmente será la dirección de algún script interno del sitio web.
- eventLabel: La respuesta completa recibida. Normalmente será el contenido HTML a cargar, aunque también puede estar en otros formatos (JSON, texto plano…) o simplemente ser una respuesta tipo OK/Error
A partir de aquí, podremos utilizar el evento personalizado ajaxSuccess como activador o trigger de otras etiquetas (eventos de Google Analytics, pixeles de marketing, código Javascript adicional…), así como sus datos asociados -URL y respuesta- para, por un lado, distinguir e identificar peticiones concretas (y así medir únicamente esas acciones o activar etiquetas concretas asociadas a las mismas), y por otro, capturar información dinámica relevante a enviar en dichas etiquetas (parámetros de evento de Analytics, por ejemplo).
Caso práctico: Medir llamadas AJAX como eventos de Google Analytics
Un primer ejemplo, sencillo y algo basto pero posiblemente útil como operación de prueba o depuración, sería medir todas las llamadas AJAX y enviarlas como eventos a una propiedad de Google Analytics.
Para ello, creamos un activador que salte en todas las instancias del evento personalizado: ajaxSuccess.
Y hecho esto, creamos una etiqueta de Google Analytics, de tipo Evento, rellenamos el ID de nuestra propiedad y demás parámetros deseados, y usamos datos dinámicos para los parámetros del evento. Por ejemplo:
Nótese que no estamos utilizando directamente la variable {{Event Label}}; no es recomendable ya que nos devuelve la respuesta completa y eso puede ser un dato muy extenso, variado, ilegible o en a saber que formato… salvo que sepamos 100% seguro lo que va a haber aquí, mala idea enviarlo a Analytics tal cual. Sí que podríamos crear alguna variable intermedia que lo procese y extraiga algo concreto (título de una alerta, URL de una imagen…) pero eso queda ya como ejercicio para el lector 😉
Por supuesto, aunque esto nos podría servir para un caso sencillo, normalmente no nos interesará medir todas las peticiones AJAX indiscriminadamente: el objetivo habitual será detectar ciertas operaciones concretas, y hacer uso de los datos asociados para identificarlas. Vamos a ver un par de ejemplos.
Caso práctico: Seguimiento de apertura de ventanas modales o popups

Un caso hipotético más concreto. Tenemos un sitio web en http://www.empresamolona.com (dominio sin registrar, ladies and gentlemen, rápido, aprovechen!) en el que en determinados momentos cargan y se muestran al usuario ventanas modales, lightboxes o (falsos) popups: Cajas de contenido temporales que aparecen superpuestas acaparando la atención del usuario, ofreciendo información, alertando de una incidencia o solicitando alguna acción a realizar. Queremos hacer seguimiento en Analytics de estas apariciones.
De salida no tenemos una forma fácil de medirlos todos con los eventos habituales de GTM ya que hay múltiples motivos que los generan: Clicks en determinados enlaces y botones, errores de validación, apertura de determinados contenidos… incluso puede que tengamos uno que aparezca automáticamente al de un rato pidiendo que nos suscribamos a una newsletter, porque somos así de cansinos.
Pero investigando un poco, hemos visto algo que tienen todos en común: Su contenido carga dinámicamente en tiempo real, por AJAX. Asi que usaremos esto para detectar su apertura.
Por suerte, como el sitio web lo hemos desarrollado nosotros mismos o alguien de total confianza que ha documentado todo extensamente y a quien podemos molestar sin restricciones, no tardaremos en saber exactamente la estructura exacta de las peticiones para identifJAJAJA bueno, vale, ahora en el mundo real. Vamos a investigarlo un poco.
Lo primero de todo, y suponiendo que el contenedor correspondiente de Google Tag Manager ya está instalado y funcionando en la web, instalamos el procesador de eventos AJAX aquí comentado y activamos el modo Preview and debug o Vista previa y depuración.
Navegamos un rato y vemos que van apareciendo los eventos ajaxSuccess. Pinchando en ellos, en la pestaña “Data Layer” vemos los datos con que se envían. Ahí podemos buscar algo común a todos los casos a medir.
En este caso de ejemplo, vamos a suponer que a todas las aperturas de popup las acompaña una llamada a un URL con la misma estructura: “/get_popup?id_popup=XXXX”. Como esto se captura en la variable {{Event Action}}, la usaremos para distinguir estos eventos de otras llamadas AJAX.
Creamos un nuevo activador, le ponemos nombre (ej. AJAX Popup) y parámetros:
- Tipo: Evento personalizado
- Nombre de evento: ajaxSuccess
- Condición adicional: {{Event Action}} contiene /get_popup
Y bingo! Ya tenemos como detectarlos.
Ahora, si queremos medir todos los popups como eventos de Google Analytics, podemos por ejemplo crear una etiqueta como esta y asociarla al activador:
O igual queremos ir un paso más allá y hacer que cada popup cuente como la carga de una página separada, enviando en este caso páginas virtuales a Analytics.
Como URL virtual (parámetro page de la etiqueta Analytics), usaremos por ejemplo directamente el de la propia petición AJAX, que normalmente será suficiente para identificar cada caso concreto.
Ya lo tenemos en {{Event Action}}, pero dado que es absoluto (http://www.empresamolona…) y normalmente a Analytics se envían rutas o URLs relativos, tendremos que crear una variable auxiliar intermedia que extraiga la ruta o pathname del dato:
Y ahora sí, la enviamos como URL virtual en una etiqueta de Analytics tipo Página vista:
Caso práctico: Páginas de confirmación virtuales
Ahora que hemos visto como detectar cargas concretas de contenido AJAX y usarlo para enviar páginas virtuales en Analytics, tendremos la capacidad de simular cargas de página donde las necesitemos y no las haya.
Por ejemplo, si tenemos un formulario de contacto que, una vez rellenado con éxito, no nos envía o redirige a una página de confirmación o “thank you page”, sino que en su lugar lo envía por AJAX y se mantiene en la página devolviendo un mensaje de OK, podremos aprovechar esta operación para generar una página de confirmación virtual que es la que a su vez podremos configurar en Analytics para objetivos, embudos de conversión, etc.
Hacemos lo mismo que en el ejemplo anterior. Creamos el procesador, lo activamos en vista previa, y vamos haciendo pruebas examinando los dataLayers que se van generando.
En este caso puede que nos encontremos con un problema extra: Todas las peticiones se envían al mismo URL, tanto los intentos fallidos (que devuelven errores de validación, de envío…) como los envíos correctos (que nos devuelven el mensaje de confirmación).
Dado que sólo nos interesa medir los segundos, necesitaremos una forma adicional de distinguirlos.
Ejemplo envío OK
Ejemplo envío KO
Por suerte, como el procesador también nos devuelve la respuesta recibida en otro parámetro, lo podemos usar para distinguirlos. Tenemos que buscar algo común a todos los casos que queremos medir pero que no esté presente en los demás – Por ejemplo, en este caso, el div class=”contacto-respuesta-ok”… y lo añadimos como condición al activador correspondiente:
- Tipo: Evento personalizado
- Nombre de evento: ajaxSuccess
- Condición adicional: {{Event Action}} contiene /contacto/enviar.php
- Condición adicional: {{Event Label}} contiene contacto-respuesta-ok
Y ya sólo nos queda crear la etiqueta correspondiente y asociarla a este nuevo activador:
Detección de eventos AJAX con Google Tag Manager SIN jQuery
Y si en el sitio web no tenemos jQuery? Podríamos incluirla desde GTM, pero además de ser una solución bastante ineficiente si no la necesitamos para nada más… no soluciona nada si las peticiones AJAX que queremos medir no se hacen a través de jQuery, sino a través de alguna otra librería o directamente Javascript puro (XMLHttpRequest).
Por suerte, si os encontrais con ese problema, aquí tenemos una solución 🙂
Al crear la etiqueta procesador de eventos, utilizar en su lugar el siguiente código (idea sacada de esta discusión de la todopoderosa e inagotable stackoverflow):
<script>
(function() {
var xhrSend = window.XMLHttpRequest.prototype.send;
window.XMLHttpRequest.prototype.send = function() {
var xhr = this;
var intervalId = window.setInterval(function() {
if(xhr.readyState != 4) {
return;
}
dataLayer.push({
'event': 'ajaxSuccess',
'eventCategory': 'AJAX',
'eventAction': xhr.responseURL,
'eventLabel': xhr.responseText
});
clearInterval(intervalId);
}, 100);
return xhrSend.apply(this, [].slice.call(arguments));
};
})();
</script>
Notas para frikis:
- El script parchea directamente la función nativa XMLHttpRequest.send(), así que será compatible con cualquier navegador que la utilice y cualquier script o librería cuya funcionalidad AJAX dependa de ella.
- Para mayor compatibilidad, no depende de la propiedad onstatechange del request sino que monitoriza directamente su estado, lo cual hace que sea compatible con practicamente todas las peticiones AJAX, incluidas las realizadas por jQuery que normalmente no funcionarían (ya que usan sus propios callbacks y pasan del onstatechange).
- Si da problemas, prueba a aumentar o reducir el tiempo de espera de la llamada setTimeout
Bonus track: Captura de datos adicionales
En algunos casos avanzados, los datos que hemos capturado hasta ahora pueden no ser suficentes para obtener todo lo que necesitamos. El más habitual es que necesitemos algo de los parámetros enviados a la llamada AJAX y no los podamos ver ya que no van dentro del URL solicitado, sino aparte en una petición de tipo POST.
Para casos como este, dejo como propina una versión un poco más compleja de ambos procesadores de eventos (con y sin jQuery) que capturan datos adicionales. Use, comparta y modifique bajo su propio criterio 😀
- event: ajaxSuccess
- eventCategory: AJAX GET, AJAX POST… según el tipo de petición HTTP
- eventAction: El URL al que se ha enviado la petición, pero INCLUYENDO los datos/parámetros enviados a la misma (separados tras un punto y coma “;”) que normalmente no veríamos si es una petición POST
- eventLabel: La respuesta recibida
Con jQuery:
<script>
(function($) {
$(document).ajaxSuccess(function( event, xhr, settings ) {
dataLayer.push({
'event': 'ajaxSuccess',
'eventCategory': 'AJAX ' + settings.type,
'eventAction': settings.url + (settings.type == 'POST' && settings.data ? ';' + settings.data : ''),
'eventLabel': xhr.responseText
});
});
})(jQuery);
</script>
Sin jQuery:
<script>
(function() {
var xhrOpen = window.XMLHttpRequest.prototype.open;
var xhrSend = window.XMLHttpRequest.prototype.send;
window.XMLHttpRequest.prototype.open = function() {
this.method = arguments[0];
this.url = arguments[1];
return xhrOpen.apply(this, [].slice.call(arguments));
};
window.XMLHttpRequest.prototype.send = function() {
var xhr = this;
var xhrData = arguments[0];
var intervalId = window.setInterval(function() {
if(xhr.readyState != 4) {
return;
}
dataLayer.push({
'event': 'ajaxSuccess',
'eventCategory': 'AJAX ' + xhr.method,
'eventAction': xhr.responseURL + (xhr.method == 'POST' && xhrData ? ';' + xhrData : ''),
'eventLabel': xhr.responseText
});
clearInterval(intervalId);
}, 100);
return xhrSend.apply(this, [].slice.call(arguments));
};
})();
</script>
Si después de esto sigues necesitando ayuda… Podría ser un buen momento para contactar con nosotros. Podemos ayudarte! 😉
Cap. 27 – Múltiples seguimientos de Analytics con GTM
Cap. 29 – Google Tag Manager para Dummies (escrito por una Dummy)
Verdaderamente acojonante, enhorabuena!! Crack… Oye, una cosa, como sabes si la peticion se realiza por javascript puro o por jquery…?? Soy un lerdo lo se…
Gracias!
Si tu web utiliza jquery (la mayoría de sitios basados en gestores de contenido lo hacen, por ejemplo), normalmente es lo que utilizarán para las llamadas AJAX: en el código habrá llamadas del tipo $(…).load(), $.ajax(…) y similares (o con “jQuery” en lugar de “$”, caso de WordPress por ejemplo).
En caso de duda, siempre tienes la opción de probar y ver si funciona 😉