Analytics

Suivre les vidéos YouTube chargées dynamiquement dans Google Tag Manager

Le suivi des vidéos YouTube dans Google Tag Manager est l’une des choses les plus utiles que vous puissiez faire en termes de suivi. YouTube a une merveilleuse API dans laquelle vous pouvez puiser et convertir les événements détectés en dataLayer messages.

Il existe de très bonnes solutions pour suivre les vidéos YouTube dans GTM :

  • La merveilleuse solution de Cardinal Path.

  • Un traitement encore plus approfondi par Bounteous.

Les deux font un excellent travail de suivi des vidéos qui ont été chargées avec la page. Cependant, les deux ont des difficultés avec le suivi dynamiquement vidéos chargées. Cela signifie des vidéos qui sont chargées paresseusement, ou dans des pop-ups, ou lorsqu’un lien de contenu est cliqué.

Suivre dynamiquement les vidéos youtube google tag manager

Dans cet article, je vais montrer comment vous pouvez suivre des vidéos chargées dynamiquement en utilisant la solution de Bounteous avec de légères modifications. Bounteous est mon groupe de geeks préféré en Amérique du Nord, et ils m’ont soudoyé avec leur livre génial juste avant Noël, ce qui serait déjà une raison suffisante pour utiliser leur code. Cependant, leur solution est également très robuste et se prête également au suivi des éléments dynamiques.

Ce dont vous aurez besoin

Les modifications seront apportées au code JavaScript dans la balise HTML personnalisée. En d’autres termes, vous ne pouvez pas utiliser le CDN de Bounteous pour cela. Vous devrez créer une balise HTML personnalisée et copier-coller le code dans le chapitre suivant.

Pour le reste, suivez simplement le guide de Bounteous. Tout cela ne fait qu’ajouter une méthode que vous pouvez déclencher lorsque les vidéos ont été chargées dynamiquement. Ne vous inquiétez pas, je vous expliquerai également la méthode plus tard.

La balise HTML personnalisée modifiée

Alors, suivez le guide Bounteous, en évitant le CDN, et quand vous arrivez au chapitre intitulé Installation du gestionnaire de balises Googlelisez plutôt ce qui suit.

Créez une nouvelle balise HTML personnalisée et donnez-lui un nom sympa comme Utilitaire – Suivi vidéo Bomb Overload Christmas Awesome.

Désolé.

Ensuite, ajoutez le code suivant.

<script> // Respectfully copied and modified from  // https://github.com/lunametrics/youtube-google-analytics //  // Original implementation by Bounteous var ytTracker = (function( document, window, config ) {    'use strict';    window.onYouTubeIframeAPIReady = (function() {          var cached = window.onYouTubeIframeAPIReady;      return function() {                if( cached ) {          cached.apply(this, arguments);        }        // This script won't work on IE 6 or 7, so we bail at this point if we detect that UA       if( !navigator.userAgent.match( /MSIE [67]\./gi ) ) {          init();             }      };    })();      var _config = config || {};   var forceSyntax = _config.forceSyntax || 0;   var dataLayerName = _config.dataLayerName || 'dataLayer';   // Default configuration for events   var eventsFired = {     'Play'        : true,     'Pause'       : true,     'Watch to End': true   };      // Overwrites defaults with customizations, if any   var key;   for( key in _config.events ) {      if( _config.events.hasOwnProperty( key ) ) {        eventsFired[ key ] = _config.events[ key ];      }    }      //*****//   // DO NOT EDIT ANYTHING BELOW THIS LINE EXCEPT CONFIG AT THE BOTTOM   //*****//    // Invoked by the YouTube API when it's ready   function init() {      var iframes = document.getElementsByTagName( 'iframe' );     var embeds  = document.getElementsByTagName( 'embed' );      digestPotentialVideos( iframes );     digestPotentialVideos( embeds );    }    var tag            = document.createElement( 'script' );   tag.src            = '//www.youtube.com/iframe_api';   var firstScriptTag = document.getElementsByTagName( 'script' )[0];   firstScriptTag.parentNode.insertBefore( tag, firstScriptTag );    // Take our videos and turn them into trackable videos with events   function digestPotentialVideos( potentialVideos ) {      var i;      for( i = 0; i < potentialVideos.length; i++ ) {              var isYouTubeVideo = checkIfYouTubeVideo( potentialVideos[ i ] );              if( isYouTubeVideo ) {         var normalizedYouTubeIframe = normalizeYouTubeIframe( potentialVideos[ i ] );         addYouTubeEvents( normalizedYouTubeIframe );       }     }    }       // Determine if the element is a YouTube video or not      function checkIfYouTubeVideo( potentialYouTubeVideo ) {     // Exclude already decorated videos          if (potentialYouTubeVideo.getAttribute('data-gtm-yt')) {              return false;     }      var potentialYouTubeVideoSrc = potentialYouTubeVideo.src || '';          if( potentialYouTubeVideoSrc.indexOf( 'youtube.com/embed/' ) > -1 || potentialYouTubeVideoSrc.indexOf( 'youtube.com/v/' ) > -1 ) {        return true;      }      return false;    }    // Turn embed objects into iframe objects and ensure they have the right parameters   function normalizeYouTubeIframe( youTubeVideo ) {          var a           = document.createElement( 'a' );         a.href      = youTubeVideo.src;         a.hostname  = 'www.youtube.com';         a.protocol  = document.location.protocol;     var tmpPathname = a.pathname.charAt( 0 ) === '/' ? a.pathname : '/' + a.pathname;  // IE10 shim          // For security reasons, YouTube wants an origin parameter set that matches our hostname     var origin = window.location.protocol + '%2F%2F' + window.location.hostname + ( window.location.port ? ':' + window.location.port : '' );      if( a.search.indexOf( 'enablejsapi' ) === -1 ) {        a.search = ( a.search.length > 0 ? a.search + '&' : '' ) + 'enablejsapi=1';      }      // Don't set if testing locally     if( a.search.indexOf( 'origin' ) === -1  && window.location.hostname.indexOf( 'localhost' ) === -1 ) {        a.search = a.search + '&origin=' + origin;      }      if( youTubeVideo.type === 'application/x-shockwave-flash' ) {        var newIframe     = document.createElement( 'iframe' );       newIframe.height  = youTubeVideo.height;       newIframe.width   = youTubeVideo.width;       tmpPathname = tmpPathname.replace('/v/', '/embed/');        youTubeVideo.parentNode.parentNode.replaceChild( newIframe, youTubeVideo.parentNode );        youTubeVideo = newIframe;      }      a.pathname       = tmpPathname;     if(youTubeVideo.src !== a.href + a.hash) {            youTubeVideo.src = a.href + a.hash;      }      youTubeVideo.setAttribute('data-gtm-yt', 'true');      return youTubeVideo;    }    // Add event handlers for events emitted by the YouTube API   function addYouTubeEvents( youTubeIframe ) {      youTubeIframe.pauseFlag  = false;      new YT.Player( youTubeIframe, {        events: {          onStateChange: function( evt ) {            onStateChangeHandler( evt, youTubeIframe );          }        }      } );    }    // Returns key/value pairs of percentages: number of seconds to achieve   function getMarks(duration) {      var marks = {};       // For full support, we're handling Watch to End with percentage viewed     if (_config.events[ 'Watch to End' ] ) {        marks[ 'Watch to End' ] = duration * 99 / 100;      }      if( _config.percentageTracking ) {        var points = [];       var i;        if( _config.percentageTracking.each ) {          points = points.concat( _config.percentageTracking.each );        }        if( _config.percentageTracking.every ) {          var every = parseInt( _config.percentageTracking.every, 10 );         var num = 100 / every;                  for( i = 1; i < num; i++ ) {                  points.push(i * every);          }        }        for(i = 0; i < points.length; i++) {          var _point = points[i];         var _mark = _point + '%';         var _time = duration * _point / 100;                  marks[_mark] = Math.floor( _time );        }      }      return marks;    }    function checkCompletion(player, marks, videoId) {      var duration     = player.getDuration();     var currentTime  = player.getCurrentTime();     var playbackRate = player.getPlaybackRate();     player[videoId] = player[videoId] || {};     var key;      for( key in marks ) {        if( marks[key] <= currentTime && !player[videoId][key] ) {          player[videoId][key] = true;         fireAnalyticsEvent( videoId, key );        }      }    }    // Event handler for events emitted from the YouTube API   function onStateChangeHandler( evt, youTubeIframe ) {       var stateIndex     = evt.data;     var player         = evt.target;     var targetVideoUrl = player.getVideoUrl();     var targetVideoId  = targetVideoUrl.match( /[?&]v=([^&#]*)/ )[ 1 ];  // Extract the ID         var playerState    = player.getPlayerState();     var duration       = player.getDuration();     var marks          = getMarks(duration);     var playerStatesIndex = {       '1' : 'Play',       '2' : 'Pause'     };     var state = playerStatesIndex[ stateIndex ];       youTubeIframe.playTracker = youTubeIframe.playTracker || {};      if( playerState === 1 && !youTubeIframe.timer ) {        clearInterval(youTubeIframe.timer);        youTubeIframe.timer = setInterval(function() {          // Check every second to see if we've hit any of our percentage viewed marks         checkCompletion(player, marks, youTubeIframe.videoId);        }, 1000);      } else {        clearInterval(youTubeIframe.timer);       youTubeIframe.timer = false;      }      // Playlist edge-case handler     if( stateIndex === 1 ) {        youTubeIframe.playTracker[ targetVideoId ] = true;       youTubeIframe.videoId = targetVideoId;       youTubeIframe.pauseFlag = false;      }      if( !youTubeIframe.playTracker[ youTubeIframe.videoId ] ) {        // This video hasn't started yet, so this is spam       return false;      }      if( stateIndex === 2 ) {        if( !youTubeIframe.pauseFlag ) {                 youTubeIframe.pauseFlag = true;        } else {          // We don't want to fire consecutive pause events         return false;        }      }      // If we're meant to track this event, fire it     if( eventsFired[ state ] ) {            fireAnalyticsEvent( youTubeIframe.videoId, state );      }    }    // Fire an event to Google Analytics or Google Tag Manager   function fireAnalyticsEvent( videoId, state ) {      var videoUrl = 'https://www.youtube.com/watch?v=' + videoId;     var _ga = window.GoogleAnalyticsObject;      if( typeof window[ dataLayerName ] !== 'undefined' && !_config.forceSyntax ) {               window[ dataLayerName ].push( {          'event'     : 'youTubeTrack',         'attributes': {            'videoUrl': videoUrl,           'videoAction': state          }        } );      } else if( typeof window[ _ga ] === 'function' &&                 typeof window[ _ga ].getAll === 'function' &&                 _config.forceSyntax !== 2 )      {        window[ _ga ]( 'send', 'event', 'Videos', state, videoUrl );      } else if( typeof window._gaq !== 'undefined' && forceSyntax !== 1 ) {        window._gaq.push( [ '_trackEvent', 'Videos', state, videoUrl ] );      }    }      return {     init : init,     digestPotentialVideos : digestPotentialVideos   }      })(document, window, {   'events': {     'Play': true,     'Pause': true,     'Watch to End': true   },   'percentageTracking': {     'every': 25,     'each': [ 10, 90 ]   } }); /*  * Configuration Details  *  * @property events object  * Defines which events emitted by YouTube API  * will be turned into Google Analytics or GTM events  *  * @property percentageTracking object  * Object with configurations for percentage viewed events  *  *   @property each array  *   Fires an event once each percentage ahs been reached  *  *   @property every number  *   Fires an event for every n% viewed  *  * @property forceSyntax int 0, 1, or 2  * Forces script to use Classic (2) or Universal(1)  *  * @property dataLayerName string  * Tells script to use custom dataLayer name instead of default  */ </script>

Oui, c’est beaucoup de choses. Soyez patient avec moi, cependant.

Les modifications apportées à la solution de Bounteous sont peu nombreuses mais significatives.

Tout d’abord, au lieu d’utiliser un fonction anonymenous réservons en fait un emplacement dans l’espace de noms global et exposons la fonction dans une variable nommée ytTracker.

Nous faisons cela parce que nous voulons invoquer certaines méthodes dans la configuration après l’exécution initiale. Si nous n’exposions pas de méthode publique pour cela, nous aurions besoin de tout exécuter encore et encore, chaque fois qu’une vidéo est chargée dynamiquement, et c’est juste une énorme pression sur les performances.

La prochaine chose que nous allons faire est d’ajouter une seule ligne dans le normalizeYouTubeIframe() méthode:

function normalizeYouTubeIframe( youTubeVideo ) {   ...   youTubeVideo.setAttribute('data-gtm-yt', 'true'); // <--- NEW    return youTubeVideo; } 

Les youTubeVideo.setAttribute() La commande est utilisée pour ajouter un attribut de données à tous les iframes qui ont déjà été traités par le script. En effet, nous voulons éviter d’exécuter encore et encore les méthodes d’initialisation pour les vidéos qui ont déjà été configurées pour le suivi. Lors de mes tests, si une vidéo était suivie plusieurs fois, cela entraînait des conflits d’exécution.

Maintenant que l’attribut de données est là, nous devons vérifier son existence. Dans le checkIfYouTubeVideo() méthode, nous ajouterons la vérification comme ceci:

function checkIfYouTubeVideo( potentialYouTubeVideo ) {       if (potentialYouTubeVideo.getAttribute('data-gtm-yt')) {     return false;   }   ... } 

Cette vérification recherche simplement le data-gtm-yt attribut que nous avons ajouté à l’étape précédente, et s’il est trouvé, l’initialisation est ignorée pour cette vidéo particulière.

De cette façon, seules les vidéos qui ont ne pas été traité mais sera traité.

Enfin, nous devons exposer deux méthodes dans le ytTracker interface. Ceux-ci nous permettront de gérer les vidéos ajoutées dynamiquement. Faisons cela à la toute fin de l’expression de la fonction.

var ytTracker = (function(...) {   ...   return {     init : init,     digestPotentialVideos : digestPotentialVideos   } })(...); 

Nous retour un objet avec deux membres : init et digestPotentialVideos. Ainsi, lorsque nous appelons ytTracker.init()le script est essentiellement exécuté à nouveau, et toutes les intégrations iframe YouTube nouvellement ajoutées, mais non traitées, seront traitées et décorées avec des trackers d’événements.

Si vous voulez qu’il soit un peu plus robuste, vous pouvez utiliser ytTracker.digestPotentialVideos(iframe) au lieu. Vous passez un objet iframe en paramètre, et le script ne traitera qu’une seule vidéo. C’est mieux car il ne parcourra pas tous les iframes de la page et décorera simplement celui que vous voulez.

Enfin, configurez la balise HTML personnalisée pour qu’elle se déclenche sur un Affichage de la page / Prêt pour le DOM Déclencheur.

Voilà les changements ici, mesdames et messieurs.

Comment l’utiliser

Comme dit, il y a deux nouvelles méthodes en ville :

ytTracker.init(); // <-- Decorate all new videos on the page ytTracker.digestPotentialVideos(iframe); // <-- Only decorate the iframe that was passed as a parameter 

Faire fonctionner la solution nécessitera très probablement l’aide d’un développeur. Si vos développeurs ont ajouté des chargeurs de contenu dynamiques complexes, qui ajoutent les vidéos après la fin d’un fondu enchaîné jQuery impressionnant, vous devrez coopérer avec eux pour ajouter le ytTracker.init() ou alors ytTracker.digestPotentialVideos() commande au bon endroit.

Voici un exemple de ce à quoi pourrait ressembler un chargeur de contenu modifié :

function processVideoClick(ytEmbedSrc) {   var video = document.createElement('iframe');   video.src = ytEmbedSrc;   var content = document.querySelector('#content');   content.appendChild(video);   window.ytTracker.digestPotentialVideos(video); } 

Une fois la vidéo injectée, vous appelez ytTracker.digestPotentialVideos(). Vous pourriez utiliser le init() méthode sans aucun paramètre, mais puisque vous avez déjà l’élément iframe à portée de main, il est préférable d’utiliser la méthode la plus directe.

Résumé

Étant donné que le chargement dynamique de contenu est notoirement sans norme, il est difficile de proposer une solution générique pour gérer le contenu chargé dynamiquement dans votre plate-forme d’analyse ou de gestion des balises. Cependant, la solution originale de Bounteous dispose d’un très bon ensemble d’outils pour s’attaquer également aux vidéos YouTube chargées dynamiquement, sans avoir à apporter de modifications drastiques au code.

La solution de Bounteous est tellement flexible et utile. Percer quelques trous dans son interface pour laisser passer la lumière le rend encore meilleur, à mon humble avis.

La manière optimale de travailler avec lui est de coopérer avec vos développeurs. J’espère que nous avons tous dépassé l’idée que GTM est indépendant des développeurs. Alors, communiquez avec vos développeurs, demandez-leur de modifier le JavaScript contrôlant l’injection vidéo dynamique, et demandez-leur d’ajouter le simple ytTracker méthodes aux gestionnaires qu’ils créent.

Source : www.simoahava.com

Articles similaires

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Bouton retour en haut de la page
Index