7 Feb 2019
Seguimiento de videos HTML5 con Google Tag Manager
Lectura: 18 mins.
|
Dificultad:

Seguimiento de vídeos HTML5 con Google Tag Manager

Si puedes ver esto, tu navegador no es del pleistoceno. No, ejemplos normales no tenemos.

Hoy os traemos otro post-receta para Google Tag Manager: una solución completa y configurable para medir vídeos HTML5 incrustados directamente en una página; es decir, con etiquetas <video> en lugar de mediante reproductores de terceros.

Aprovechando que ya tenemos seguimiento de vídeos integrado en GTM (aunque de momento solo para vídeos de Youtube), este procesador de eventos es compatible con este tracking nativo y simula en la medida de lo posible su estructura y funcionamiento.

Esto significa que, una vez instalado, no solo podremos detectar las interacciones deseadas con estos vídeos mediante eventos personalizados GTM, sino que funcionarán de la misma forma que el seguimiento existente para Youtube:

  • Los eventos y dataLayers generados seguirán la misma estructura de datos y variables.
  • Se rellenarán correctamente las variables integradas asociadas como {{Video URL}}, {{Video Status}} y demás.
  • E incluso podremos reutilizar etiquetas y activadores y hacer seguimiento unificado de ambos tipos de vídeos en Analytics y otros servicios.

¿Qué diferencia a estos vídeos, y sus posibilidades de tracking, de los proporcionados por Youtube y otras plataformas?

1. La etiqueta <video>

La etiqueta <video> de HTML5 nos permite incrustar archivos de vídeo que tengamos alojados directamente en páginas HTML/XHTML, sin necesidad de servicios externos ni scripts adicionales.

Entre otros parámetros, permite especificar varias fuentes (archivos) distintas para que cada navegador utilice el formato más oportuno y maximizar la compatibilidad, así como otros datos adicionales de configuración, como imagen de portada, controles de reproducción, pistas de subtítulos y otros.

Así, según la información y fallbacks adicionales aportados, la etiqueta a incluir para incrustar un vídeo puede ser tan compleja como:

<video autoplay controls loop width="400" height="350" poster="portada.png">
  <source src="video.webm" type="video/webm">
  <source src="video.mp4" type="video/mp4">
  <source src="video.ogg" type="video/ogg">
  <track default kind="subtitles" src="subtitulos_es.vtt" srclang="es">
  Tu navegador no permite la visualización del video
  (aquí podría ir algún método alternativo de reproducción)
</video>

O tan sencilla como:

<video src="video.webm"></video>

La principal ventaja de incrustar así los vídeos consiste en el control total sobre los mismos; no solo sobre el propio contenido —que normalmente alojaremos nosotros mismos—, sino sobre los propios elementos de reproducción, su diseño y posibilidades de personalización y seguimiento.

La desventaja, evidentemente, es que esa personalización y seguimiento los tendremos que hacer nosotros 😜.

Y quien dice “personalización y control total”, dice “a merced de los designios del navegador de turno”, claro 🤷‍♀️.

Normalmente, cuando queremos medir interacciones con vídeos incrustados, el problema que tenemos es que dependemos de plataformas externas (Youtube, Vimeo, Wistia…) y de las opciones de analítica e integración que nos ofrezca cada una; los propios vídeos utilizan habitualmente tecnología basada en elementos “ajenos” a la página, como iframes o scripts externos, que no nos permitirán observarlos directamente.

En este caso no tendremos este problema: los vídeos son elementos HTML estándar de la página y forman parte de la estructura DOM de la misma, incluida la generación automática de sus propios eventos internos, que podemos detectar y manipular vía Javascript.

Así que lo “único” que tendremos que hacer, ya que GTM no genera automáticamente activadores en base a esos eventos DOM, será detectarlos y medirlos en GTM mediante event listeners o procesadores de eventos personalizados, similares a los que ya vimos en este artículo.

2. Instalación y configuración

Para este caso, utilizaremos un procesador de eventos personalizado que, además de detectar las acciones relevantes, recopile toda la información posible sobre el vídeo y su estado actual, incluido el tiempo de reproducción transcurrido.

Los pasos a seguir serán:

1) Creamos una etiqueta de tipo HTML Personalizado/Custom HTML e introducimos el siguiente código:

<script>
(function() {
	var trkEvent = 'gtm.video'; // nombre de evento a generar
	var trkProvider = 'html5'; // Video Provider a mostrar
	var trkElements = 'video'; // DOM query de elementos a medir, normalmente video y/o audio
	var trkEvents = ['playing','pause','ended','seeked','waiting','timeupdate']; // eventos a medir
	var trkPercents = [25,50,75]; // porcentajes de progreso a medir
	var trkSeconds = []; // puntos de progreso en segundos a medir
	var trkUseCapture = true; // capture vs bubble
	var trkLive = false; // Hay que capturar elementos dinámicos?
	var trkLiveContainer = 'body'; // Contenedor superior que contenga los elementos dinámicos. Vacio = document

	function processVideoEvent(e) {
		var v = e.target;
		if(v.matches(trkElements)) {
			delete v.trackedTime;
			delete v.trackedProgress;
			switch(e.type) {
				case 'timeupdate':
					if(trkPercents.length) {
						v.percent = Math.floor(100 * (v.currentTime / v.duration));
						for(p = 0, pl = trkPercents.length; p < pl; p++) { if(!v.classList.contains('tracked-percent-' + trkPercents[p]) && v.percent >= trkPercents[p]) {
								v.classList.add('tracked-percent-' + trkPercents[p]);
								v.trackedTime = trkPercents[p];
								v.trackedProgress = '%';
								v.status = 'progress';
								pushVideoEvent(e);
							}
						}
					}
					if(trkSeconds.length) {
						for(p = 0, pl = trkSeconds.length; p < pl; p++) { if(!v.classList.contains('tracked-seconds-' + trkSeconds[p]) && v.currentTime >= trkSeconds[p]) {
								v.classList.add('tracked-seconds-' + trkSeconds[p]);
								v.trackedTime = trkSeconds[p];
								v.trackedProgress = 's';
								v.status = 'progress';
								pushVideoEvent(e);
							}
						}
					}
				break;
				
				case 'playing':
					if(!v.classList.contains('tracked-start')) {
						v.classList.add('tracked-start');
						v.classList.remove('tracked-complete');
						v.status = 'start';
						pushVideoEvent(e);
					}
				break;

				case 'ended':
					if(!v.classList.contains('tracked-complete')) {
						v.classList.add('tracked-complete');
						v.classList.remove('tracked-start');
						v.status = 'complete';
						pushVideoEvent(e);
					}
				break;

				case 'pause':
					if(v.currentTime != v.duration) { // ignorar stop automatico
						v.status = 'pause';
						pushVideoEvent(e);
					}
				break;

				case 'seeked':
					v.status = 'seek';
					pushVideoEvent(e);
				break;

				case 'waiting':
					v.status = 'buffering';
					pushVideoEvent(e);
				break;
			}			
			if({{Debug Mode}}) console.log(e);
		}
	}
	
	function pushVideoEvent(e) {
		var v = e.target, vpercent, vctime;
		// calculo sencillo de visibilidad - desactivar si no es necesario
		var vrect = v.getBoundingClientRect();
		v.visible = (vrect.top >= 0 && vrect.bottom <= window.innerHeight);
		// actualizar tiempos
		if(v.trackedProgress == '%') {
			vpercent = v.trackedTime;
			vctime = Math.floor((vpercent / 100) * v.duration);
		} else if(v.trackedProgress == 's') {
			vctime = v.trackedTime;
			vpercent = Math.floor(100 * (vctime / v.duration));
		} else {
			vctime = Math.floor(v.currentTime);
			vpercent = Math.floor(100 * (v.currentTime / v.duration));
		}
	
		var dle = {
			'event': trkEvent,
			'gtm.element': v,
			'gtm.elementClasses': v.className || '',
			'gtm.elementId': v.id || '',
			'gtm.elementTarget': v.target || '',
			'gtm.elementUrl': v.currentSrc,
			'domEvent': e,
			'gtm.videoProvider': trkProvider,
			'gtm.videoStatus': v.status,
			'gtm.videoAction': v.status == 'progress' ? v.status + '_' + v.trackedTime + v.trackedProgress: v.status,
			'gtm.videoUrl': v.currentSrc,
			'gtm.videoTitle': v.title || v.currentSrc || '',
			'gtm.videoDuration': Math.floor(v.duration),
			'gtm.videoCurrentTime': vctime,
			'gtm.videoPercent': vpercent,
			'gtm.videoTrackedProgress': v.trackedProgress,
			'gtm.videoTrackedTime': v.trackedTime,
			'gtm.videoVisible': v.visible
		};
		dataLayer.push(dle);
	}

	var trkTarget = trkLive ? trkLiveContainer : trkElements;
	var trk = trkTarget == 'window' ? [window]
		: trkTarget == 'document' ? [document]
		: trkTarget ? document.querySelectorAll(trkTarget) : [document];
	for(i = 0, il = trk.length; i < il; i++) {
		for(j = 0, jl = trkEvents.length; j < jl; j++) {
			trk[i].addEventListener(trkEvents[j], processVideoEvent, trkUseCapture);
		}
	}
})();
</script>

2) Editamos las primeras líneas del script, correspondientes a variables de configuración, para adaptarlo a nuestras necesidades. Esta es la explicación completa de cada campo configurable:

  • trkEvent: el nombre del evento personalizado GTM a generar; podemos dejar gtm.video para que sea igual que el del tracking Youtube integrado o usar otro para distinguirlo mejor.
  • trkProvider: el texto que devolverá la variable {{Video Provider}}. Podemos, por ejemplo, poner algo específico a nuestra web o empresa, o incluso algo dinámico como {{Page Hostname}}.
  • trkElements: los elementos a observar en formato “CSS Query” o selector DOM. Normalmente “video” para todas las etiquetas <video>, aunque también podemos acotar el seguimiento a clases o IDs especificos (ej: “video.trackme” para <video class="trackme">) o añadir “audio” para medir también elementos incrustados de audio (ver más abajo).
  • trkEvents: los eventos DOM a observar. Los aquí indicados son los necesarios para medir todas las acciones disponibles en el activador Youtube Video equivalente; lo correcto será dejar únicamente los que necesitemos:
    • playing, pause y ended para inicio, pausa y finalización de reproducción respectivamente.
    • Podemos quitar seeked y waiting si no queremos medir saltos manuales ni pausas por buffering.
    • Quitar timeupdate si no necesitamos medir % de reproducción.
  • trkPercents: si queremos medir si se han alcanzado determinados puntos de reproducción, poner los hitos deseados aquí, separados por comas y expresados en porcentaje de la duración total. Por ejemplo, poniendo [25,50,75] saltarán eventos cuando se alcance el 25%, 50% y 75% del total respectivamente (el 100% ya está cubierto por complete, pero también lo podemos añadir aquí si queremos).
  • trkSeconds: como trkPercents, pero para medir progreso en tiempo total en segundos. Se pueden combinar ambos.
  • trkUseCapture: afecta a la forma de procesado de eventos, ver más información. Cambiar solo si da problemas, dejar en true si también marcamos trkLive.
  • trkLive: ampliar seguimiento a elementos creados dinámicamente (no presentes aún en la carga del procesador, por lo general generados a posteriori en tiempo real; ver más abajo). Activar solo cuando sea necesario.
  • trkLiveContainer: si trkLive está activado, acotar el seguimiento únicamente a elementos creados dentro de este elemento contenedor (identificado por selector CSS). Podemos dejarlo en global (body o document) o acotar a algo más específico (ej. “#content” o “.post” en WordPress)

3) Añadimos un activador. Normalmente nos interesará activarlo tan pronto como la estructura HTML de la página esté disponible, por tanto utilizamos uno de carga de página de tipo DOM Preparado/DOM Ready, que muy posiblemente ya tengamos creado:

Activador DOM Ready

Por supuesto, en este punto podemos (¡debemos!) aprovechar para limitar la carga del procesador únicamente a las páginas o secciones en las que lo necesitemos para no afectar negativamente al rendimiento. Para ello, como de costumbre, podremos añadir condiciones adicionales al activador con variables de página tipo {{Page URL}} y similares.

ejemplo condicion adicional activador
Por ejemplo.

Ponemos un nombre descriptivo, ej. Procesador Video y guardamos.

Procesador de eventos de video
Debería quedar algo talqueasí.

4) Finalmente, solo quedará activar las variables en las que se guardarán los datos asociados a cada interacción: estado del vídeo, datos identificativos del mismo, tiempo transcurrido, etc.

Dado que estos datos se guardarán automáticamente en las mismas variables integradas para el seguimiento de Youtube ya existentes, no tendremos que crear nuevas, sino únicamente entrar en el panel de Variables > Variables Predefinidas/Built-in Variables y activarlas, si no lo estuvieran ya:

Variables integradas para Videos
Activar estas.

3. Funcionamiento y pruebas

Una vez creado el procesador de eventos, si activamos la vista previa de GTM en cualquier página en la que tengamos un vídeo incrustado, por ejemplo este que corresponde al que adorna este post 😜…

<video controls src="https://aukera.es/blog/imagenes/media/navidance.mp4" width="350" height="300" title="Vidiotez Random"></video>

… y empezamos a interaccionar con él, deberíamos ver cómo van apareciendo eventos gtm.video (o el nombre que hayamos elegido) en la consola de depuración de Tag Manager, con sus llamadas a dataLayer correspondientes:

Eventos gtm.video en consola GTM

Y veremos también que las variables integradas asociadas se rellenan correctamente:

Variables de evento gtm.video en consola GTM

Con eso ya tendremos correctamente detectadas las interacciones y automáticamente rellenados los siguientes datos que podremos utilizar en activadores y etiquetas de Google Analytics o cualquier otro servicio.

  • El nombre de evento elegido como evento personalizado GTM y en {{Event}}
  • El estado actual al que acaba de cambiar el vídeo en {{Video Status}}, para identificar la acción concreta: start, pause, progress…
  • Variables adicionales identificativas: {{Video URL}}, {{Video Title}}, {{Video Duration}}
  • Variables adicionales sobre el estado actual: {{Video Percent}}, {{Video Current Time}}, {{Video Visible}}

Así, como norma general, para lanzar cualquier etiqueta basada en las interacciones medidas, bastará crear un activador de evento personalizado con el mismo nombre que le hayamos puesto en el procesador:

Activador gtm.video

Y a partir de ahí añadir variables adicionales como condiciones de activador o parámetros de etiqueta que queramos lanzar.

4. Caso general: seguimiento en Google Analytics

Así, por ejemplo, para medir las interacciones con vídeos HTML como eventos de Google Analytics, de la misma forma y con el mismo formato con que lo hicimos aquí con los vídeos de Youtube, bastará con:

  1. Crear y configurar la etiqueta procesador con los eventos que queremos medir.
  2. Crear el activador genérico gtm.video.
  3. Crear la etiqueta de evento de Google Analytics asociada.

Los dos primeros pasos ya los hemos visto. En cuanto a la etiqueta de GA, únicamente necesitaremos una general de tipo Evento de Universal Analytics, con la misma configuración común que presuponemos ya creada para otras etiquetas, y los parámetros de evento que queramos ponerle, incluido {{Video Status}} para identificar la acción realizada:

Etiqueta GA para eventos de video

Como siempre, ojo al parámetro de Hit sin interacción/Non-Interaction Hit: aunque normalmente nos interesa dejarlo desactivado (false), debemos activarlo (true) si tenemos algún vídeo con reproducción automática, ya de que lo contrario nos cargaremos la tasa de rebote para las páginas que lo contengan. Más información sobre la tasa de rebote aquí.

Nótese que, por lo general, si queremos medir diferentes puntos de progreso de reproducción, probablemente no nos resultará suficiente enviar progress como acción, así en genérico, en todos los casos, sino que querremos distinguir entre las diferentes acciones de progreso añadiendo el porcentaje o punto de reproducción alcanzado en cada caso.

Para ello, el procesador de eventos ya ha previsto esta situación y distingue entre diferentes acciones de progreso en una variable interna separada: gtm.videoAction. Si queremos utilizarla directamente, bastará con crear una nueva variable personalizada de tipo Capa de Datos/Data Layer para acceder a ella y utilizarla en lugar de {{Video Status}} como parámetro identificativo de acción de evento:

variable gtm.videoAction

Alternativamente, si queremos además utilizar diferentes nombres de acción definidos por nosotros para cada evento, podemos hacer como hicimos aquí y crear una variable de tabla de consulta intermedia que convierta cada valor de {{Video Status}} al nombre de parámetro que hayamos decidido previamente en la guía de etiquetado correspondiente. Por ejemplo:

tabla de consulta para accion de video

5. Otros casos: tracking de conversiones

Para otros servicios de analítica, el tracking general de vídeos funcionará de forma similar, únicamente cambiando el tipo de etiqueta y su estructura.

Si lo que queremos es medir diferentes casos para diferentes etiquetas, por ejemplo, todas las interacciones de todos los vídeos (para analítica general) vs una acción de un vídeo concreto (para una etiqueta de conversión basada en un objetivo en particular), una posible solución sería crear varias copias separadas del procesador de eventos y poner nombres de eventos diferentes para cada una, pero eso sería bastante ineficiente. 🙁

En su lugar, lo más recomendable será asegurarnos de que el procesador esté correctamente configurado para cubrir todos los casos que queramos medir y distinguir entre ellos creando diferentes activadores con condiciones adicionales basadas en las variables asociadas.

Por ejemplo, para medir un objetivo definido como “Reproducir el vídeo portada.mp4 hasta por lo menos el 75%“, podríamos crear el siguiente activador:

Ejemplo activador de video personalizado

6. Otras configuraciones, recomendaciones y notas

Seguimiento unificado Youtube + HTML5

Si nuestro sitio web combina vídeos propios incrustados y vídeos de Youtube, podemos medir todos de forma unificada, sin necesidad de mantener etiquetas duplicadas. Para ello, los pasos a seguir son sencillos:

  1. Configurar el tracking de vídeos HTML5 aquí indicado.
  2. Crear un activador de tipo Video Youtube, configurado de forma que mida las mismas acciones que hayamos configurado en el procesador. NO es necesario añadirlo a ninguna etiqueta, este paso es necesario únicamente para que GTM active su tracking interno de vídeos de Youtube.

A partir de ahí, no hay que hacer nada más: las mismas etiquetas de seguimiento de vídeos que hayamos creado también medirán automáticamente vídeos de Youtube. Esto funciona porque el seguimiento integrado de Youtube generará eventos con el mismo nombre (gtm.video) y variables asociadas que el procesador.

¡Ojo! Esto no funciona en dirección contraria: los activadores de tipo Video Youtube NO saltan con todos los eventos gtm.video en general. Esto es así porque incluyen comprobaciones internas adicionales que permiten distinguir entre diferentes triggers.

Etiquetas <audio>

Además de <video> para elementos audiovisuales, HTML5 también proporciona la etiqueta <audio> para incrustar archivos de sonido.

Dado que internamente funcionan de forma similar, este mismo procesador también sirve para medir archivos de audio incrustados: solo hay que añadirlos a la lista de elementos a medir.

Por ejemplo, para medir todas las etiquetas <audio> y <video> con el mismo procesador:

var trkElements = 'audio,video';

A tener en cuenta…

Como ya hemos comentado, en general el procesador intenta que su funcionamiento sea lo más parecido posible al del tracking integrado de Youtube, para mayor compatibilidad entre ambos y mayor posibilidad de integración unificada con otros proveedores/plataformas de contenido.

Sabiendo esto, algunas particularidades de funcionamiento interno del procesador a tener en cuenta son:

*Más conocidas como las sospechosamente traducidas “Variables de evento automático”.
  • La acción start solo salta una vez por vídeo: cuando empieza a reproducirse por primera vez (sea de forma automática o manual).
  • Por el contrario, la acción pause salta cada vez que se interrumpe la reproducción, excepto cuando lo hace de forma automática al llegar al final.
  • No solo las variables integradas especificas de vídeo ({{Video …}}) están disponibles en cada dataLayer generado: también las variables automáticas de evento*, como {{Click Element}}, {{Click ID}} o {{Click Classes}}, se rellenarán con los datos de la propia etiqueta <video> asociada al evento y las podremos utilizar en etiquetas y activadores.
  • Para mayor consistencia, todos los eventos de progreso de reproducción saltan en orden si se rebasan, aunque se hayan pasado de largo. Es decir, que si saltamos manualmente al 80% de la duración de reproducción, los eventos correspondientes al 25%, 50% y 75% saltarán inmediatamente y en ese orden. Ojo, en esto el seguimiento sí difiere del integrado de Youtube, que solo mide el último punto alcanzado de forma “natural”. Así mismo, en esos casos las variables {{Video Current Time}} y {{Video Percent}} se reescriben para reflejar cada punto de tiempo alcanzado retroactivamente.
  • Las acciones de progreso de reproducción no miden tiempo de reproducción real, sino únicamente que se hayan alcanzado o rebasado, manual o automáticamente, los puntos indicados: así, si avanzamos manualmente al final del vídeo, saltarán de golpe todos los eventos progress y complete que tengamos configurados. El tiempo de reproducción real es un poco más complejo de medir —el tracking Youtube sí lo hace, parcialmente, mediante la variable interna oculta gtm.videoElapsedTime— y queda para implementaciones a medida que sí lo requieran.
  • sí, he esperado años para usar la parida recurrente, qué pasa
    ¿¡ALGUIEN HA DICHO AJAX!?
    Si el procesador parece no responder para algún vídeo concreto, hay varios posibles motivos, pero uno de los más probables es que el vídeo en cuestión no estuviera aún cargado cuando se activó la etiqueta de procesador de eventos y, por tanto, no se llegó a crear su event listener correspondiente.

    Esto ocurre en el caso de vídeos que carguen dinámicamente a posteriori por múltiples motivos: plantilla con carga de contenido progresivo o lazy-loading, contenidos dinámicos AJAX tipo scroll infinito, elemento de vídeo que carga bajo demanda en el momento del clic… En cualquier caso, la solución es la misma: prueba a activar (poner a true) las variables de configuración trkLive y trkUseCapture.

    Esto hace que los event listeners no se aten al propio elemento de vídeo, sino a todo el documento (de hecho, al elemento indicado en trkLiveContainer), con lo que se aplican retroactivamente incluso a elementos que no existen aún. Esto sin embargo afecta negativamente al rendimiento de la página, así que ¡activar solo cuando sea necesario!

Enlaces de interés

sabidulia milenalia
Y de regalo por haber llegado hasta aquí, otra perla de sabiduría milenaria.