Introduction
Google Analytics est un outil d’une puissance inégalée et simple à mettre en œuvre pour mesurer l’audience d’un site Web (visites, pages vues, vitesse des pages, localisation géographique des visiteurs, systèmes d’exploitation…).
Après avoir créé et défini son compte Google Analytics UA-XXXXXXX-X
pour son site Web, l’implémentation est aisée et asynchrone. Quelques lignes de code Javascript suffisent pour charger la librairie des API Google Analytics (http://www.google-analytics.com/ga.js
ou dc.js
si la mesure des données démographiques et centres d’intérêt ont été activées).
Mais les technologies et les API évoluent… les librairies ga.js
et dc.js
de Google Analytics deviennent obsolètes. Il faut migrer vers la nouvelle librairie analytics.js
, aussi appelée Google Analytics Universal. Les nouveautés de Google Analytics Universal ne sont pas flagrantes, mais la migration vers cette librairie est incontournable, les anciennes librairies ga.j
et dc.js
, encore actives (Septembre 2016), n’offriront pas de nouvelles fonctionnalités.
Quels sont les impacts de la migration ? Si l’appel de l’ancienne librairie ga.js
est centralisé dans un script Javascript, ce qui doit être généralement le cas, la migration est très rapide. Lorsque des fonctionnalités plus avancées ont été implémentées avec la librairie ga.js
(mesure des téléchargements de documents PDF, scripts, définition d’évènements…), les choses se corsent un tout petit peu.
La migration vers les API analytics.js
est abordée ici avec toutes ses spécificités (mesure des téléchargements, évènements…), et elle aboutit très souvent à une réflexion sur un mécanisme moins dépendant des changements éventuels des API de Google Analytics. Une automatisation des actions Google Analytics (tracking de téléchargements, évènements…) avec une fonction javascript unique est proposée dans cet article à travers un cas pratique en exploitant les attributs rel
et data-%
des balises des liens hypertextes <a>
. Bien entendu l’utilisation des attributs data-%
implique que la migration vers HTML 5 soit terminée.
Le moindre changement dans les API de Google Analytics sera répercuté dans cette fonction Javascript (rebuild_href
).
L’ancien code avec ga.js et dc.js
Google Analytics était mis en œuvre avec le morceau de code Javascript ci-dessous dans l’entête de la page pour mesurer la visite :
<head> … <script type="text/javascript">
var _gaq = _gaq || []; var gaCode="UA-xxxxxx-x"; _gaq.push(['_setAccount',gaCode]); _gaq.push(['_trackPageView']); var v_gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); var v_src_ga = document.createElement('script'); v_src_ga.setAttribute("type","text/javascript"); v_src_ga.setAttribute("src",v_gaJsHost + "google-analytics.com/ga.js"); v_src_ga.setAttribute("async","true"); var v_script_first = document.getElementsByTagName('script')[0]; v_script_first.parentNode.insertBefore(v_src_ga,v_script_first);
</script> … </head>
- Un objet javascript
_gaq
est créé. - Le compte Google Analytics
UA-XXXXXXX-X
est donné avec la méthode_setAccount
. - La mesure de la page est indiquée avec la méthode
_trackPageView
. - Enfin un élément
<script>
est ajouté dynamiquement dans l’arbre DOM de la page afin de charger la librairie Google Analyticshttp://www.google-analytics.com/ga.js
(dc.js
si les données démographiques et centres d’intérêt ont été activés). - Lorsque la librairie Google Analytics est chargée avec succès, les méthodes
_setAccount
et_trackPageView
données en amont en asynchrone avec la méthodepush
sont exécutées.
Afin de ne pas reproduire ce code en dur dans toutes les pages, une librairie générique lib.js
chargée automatiquement par toutes les pages s’occupe de cette cinématique.
<head>
…
<script type="text/javascript> src="lib.js"></script>
…
</head>
lib.js
var _gaq = _gaq || [];
_gaq.push(['_setAccount',gaCode]);
_gaq.push(['_trackPageView']);
var v_src_ga = document.createElement('script');
…
var v_script_first = document.getElementsByTagName('script')[0];
v_script_first.parentNode.insertBefore(v_src_ga,v_script_first);
Migration vers analytics.js
Le chargement de la librairie analytics.js
est réalisé avec le code ci-dessous, code un peu moins lisible que celui du chargement de la librairie ga.js
:
lib.js
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-XXXXXXX-X');
ga('send', 'pageview');
L’objet javacript _gaq
et sa méthode push
disparaissent pour être remplacé par l’objet ga
.
_gaq.push(['_setAccount','UA-XXXXXXX-X']); ga('create','UA-XXXXXXX-X');
_gaq.push(['_trackPageView']); ga('send','pageview');
Par défaut, la mesure de la vitesse de chargement des pages est réalisée sur un échantillon de 1% des visiteurs. En fonction du nombre de visites par jour, le pourcentage doit être ajusté pour obtenir une moyenne plus juste de la vitesse des pages. Pour un site avec moins de 500 visites par jour, il est préférable d’un point de vue statistiques d’ajuster l’échantillonnage à 100%. La syntaxe de la modification de cette propriété (siteSpeedSampleRate
) est différente entre ga.js
et analytics.js
_gaq.push(['_setSiteSpeedSampleRate', 100]); ga('create','UA-XXXXXXX-X',{'siteSpeedSampleRate': 100});
Avec les nouvelles API analytics.js
, la propriété siteSpeedSampleRate
ne peut être définie qu’avec l’option create
de l’objet ga
(analytics.js, Create Only Fields). C’est le cas aussi de la propriété sampleRate
qui correspond au pourcentage des visiteurs analysés, par défaut cette propriété est à 100%, pour l’ajuster à 10% lorsqu’il s’agit d’un site à très fort trafic :
_gaq.push(['_setSampleRate', 10]); ga('create','UA-XXXXXXX-X',{'sampleRate': 10});
Plusieurs propriétés peuvent être données dans l’option create
puisqu’il s’agit d’un tableau Javascript.
ga('create','UA-XXXXXXX-X',{'sampleRate': 10, 'siteSpeedSampleRate': 100});
La migration vers analytics.js
est rapide si le code de tracking avec l’ancienne librairie ga.js
était déjà centralisé dans un script générique pour toutes les pages. Tout devient moins simple si des fonctionnalités avancées étaient utilisées.
Migration des mesures sur les téléchargements (documents PDF, scripts)
Il n’y a pas que des pages HTML sur son site Web. Avec les API de Google Analytics, on peut mesurer les téléchargements de ses documents PDF, scripts… et afficher ceux-ci comme des pages vues. Avec la librairie ga.js
, le code est simple et l’attribut onclick
des liens hypertextes est exploité. L’existence de l’objet _gaq
est détectée et la méthode _trackPageView
est appelée en donnant l’URL du document en paramètre (this.href
).
<a href="sybase-ase-15.0.2-guide-pratique-tuning.pdf" rel="external"
onclick="if (typeof _gaq != 'undefined') { _gaq.push(['_trackPageview',this.href]); }">
Sybase ASE 15.0.2, guide pratique et outils de diagnostic
</a>
Lors des migrations vers analytics.js
, la cinématique change peu. Ce n’est plus l’objet _gaq
qui est testé mais l’objet ga
.
<a href="fichier.pdf"
onclick="if (typeof _gaq != 'undefined') { _gaq.push(['_trackPageview',this.href]); }">
<a href="fichier.pdf"
onclick="if (typeof ga != 'undefined') { ga('send','pageview',this.href); }">
Et là les ennuis commencent, les appels avec l’ancien code _gaq.push(['_trackPageview'])
ont été réalisés un peu partout en dur (fichiers HTML, scripts PHP…). Vous n’imaginiez pas à l’époque que les API Google Analytics changeraient et on ne vous y reprendra plus.
Avec vos outils préférés (Notepad++, ActiveState Komodo Edit…) ou encore les commandes systèmes Unix/Linux ( find
, grep
…) également disponibles avec MinGW sur les plateformes Windows, la revue d’impact est réalisée :
% find -regex '.*\.\(htm\|html\|inc\|js\|php\)' -print | xargs grep -i '_trackPageview' | wc -l
60
Et le résultat tombe : environ 60 fichiers à migrer.
Migration des évènements
Des évènements ont probablement été définis avec ga.js
, c’est le cas notamment pour traquer et mesurer les clics sur les liens externes au site. Dans l’attribut onclick
du lien, l’objet _gaq
est testé et l’évènement est envoyé avec _trackEvent
:
<a href="http://www.sybase.com"
onclick="if (typeof _gaq != 'undefined') { _gaq.push(['_trackEvent', 'outgoing clic', this.href]) };">
Comme pour la mesure des téléchargements, la migration des évènements vers analytics.js
est intuitive.
<a href="url"
onclick="if (typeof _gaq != 'undefined') { _gaq.push(['_trackEvent', 'outgoing clic', this.href]); }">
<a href="url"
onclick="if (typeof ga != 'undefined') { ga('send','event','outgoing clic', this.href); }">
La revue d’impact conclut également à un volume de fichiers à migrer assez conséquent.
Fonction javascript pour automiser Google Analytics et minimiser les futurs changements d’API
Dans la migration, au lieu de remplacer les appels à _gaq
et _gaq.push((['_trackPageview'/'_trackEvent']))
par ga
et ga('send','pageview'/'event')
, que ces appels soient codés en dur ou générés dynamiquement par PHP par exemple, le code javascript de l’attribut onclick
des liens hypertextes en relation avec Google Analytics va être tout simplement supprimé pour être remplacé par les attributs natifs rel
et data-%
, attributs qui seront analysés par une fonction Javascript (rebuild_href
) afin d’appliquer les actions Google Analytics adéquates.
Le cahier des charges de la future fonction rebuild_href
est alors établi :
Contexte | Fonction rebuild_href |
---|---|
Pour tous les liens hypertextes externes au site
(outgoing clic), la valeur nofollow est donnée
à l’attribut rel .
|
Lorsque la propriété nofollow est rencontrée dans l’attribut rel ,
la fonction déclenche un évènement 'outgoing clic' :
|
Pour le téléchargement des documents PDF, aucun
attribut spéficique à Google Analytics n’est défini
pour le lien.
|
Lorsque la propriété nofollow n’existe pas pour l’attribut rel
et que l’URL du lien finit par l’extension .pdf , la fonction déclenche
une visite pageview du document PDF et un évènement
'pdf download' :
|
Pour les téléchargements de scripts (.sql, .txt …),
centralisés dans le répertoire ../scripts , aucun
attribut spéficique à Google Analytics n’est défini
pour le lien.
|
Lorsque la propriété nofollow n’existe pas pour l’attribut rel
et que l’URL contient le répertoire ../scripts , la fonction déclenche
une visite pageview du script et un évènement 'script download' :
|
La fonction rebuild_href
ne doit pas traiter systématiquement l’intégralité des liens dans une page, on souhaite pouvoir cibler les blocs dans une page pour lesquels les liens doivent avoir une interaction avec Google Analytics. Par exemple, les liens du doivent échapper à cette fonction. Tout va dépendre du squelette de la page.
Avec le squelette de démo ci-dessus, la fonction rebuild_href
ne sera appliquée que pour les liens dans les blocs <article>...</article>
et <section id="theme">...</section>
.
La syntaxe de la fonction rebuild_href
sera donc la suivante :
rebuild_href('<tag>' | '<id>' | '<classe CSS>');
Exemples d’appels :
rebuild_href('article');
rebuild_href('theme');
Un identifiant d’un élément HTML, le nom d’une balise ou d’une classe CSS est donné en paramètre de la fonction rebuild_href
:
rebuild_href = function(tagoridorclass) {
var v_element= [];
v_element[0] = document.getElementById(tagoridorclass);
if (v_element[0] === null) { v_element = document.getElementsByTagName(tagoridorclass); }
if (v_element === null) { v_element = document.getElementsByClassName(tagoridorclass); }
if (v_element === null) { return; }
...
};
On recherche d’abord si il s’agit d’un élément avec un id
avec la méthode getElementById
, si ce n’est pas le cas le ou les éléments avec une balise avec la méthode getElementsByTagName
. Si il n’y a toujours pas d’éléments, la recherche d’éléments est réalisée sur la classe css avec la méthode getElementsByClassName
. La fonction est quittée (return
), si la recherche d’objets avec ces 3 méthodes n’a pas abouti.
La problèmatique ne se pose pour les balises, mais pour les id et les classes,
les conflits de nom doivent être évités (id="download", class="download"
).
Une boucle démarre pour rechercher les liens hypertextes dans ces éléments avec la méthode getElementsByTagName('a')
:
rebuild_href = function(tagoridorclass) {
var v_element= [];
v_element[0] = document.getElementById(tagoridorclass);
if (v_element[0] === null) { v_element = document.getElementsByTagName(tagoridorclass); }
if (v_element === null) { v_element = document.getElementsByClassName(tagoridorclass); }
if (v_element === null) { return; }
for (i=0; i < v_element.length; i++) {
v_href = v_element[i].getElementsByTagName('a');
if (v_href.length > 0) {
...
}
}
};
Si des liens sont trouvés (v_href.length > 0
), le code javascript d’envoi de l’évènement 'outgoing clic' est généré pour chaque lien si l’attribut rel
existe et contient la propriété nofollow
, code javascript qui est ensuite appliqué à l’attribut onclick
du lien avec la méthode setAttribute
:
rebuild_href = function(tagoridorclass) {
var v_element= [];
v_element[0] = document.getElementById(tagoridorclass);
if (v_element[0] === null) { v_element = document.getElementsByTagName(tagoridorclass); }
if (v_element === null) { v_element = document.getElementsByClassName(tagoridorclass); }
if (v_element === null) { return; }
for (i=0; i < v_element.length; i++) {
v_href = v_element[i].getElementsByTagName('a');
if (v_href.length > 0) {
for (j=0; j < v_href.length; j++) {
var jscode=" ";
if (v_href[j].getAttribute('rel') !== null) {
aInnerHtml = v_href[j].innerHTML.replace(/(\s{2,}|\t{1,})/g,' ').replace(/(\r\n|\n|\r)/g,'');
if ( v_href[j].getAttribute('rel').indexOf('nofollow') != -1 ) {
jscode += "if (typeof ga != 'undefined') { ";
jscode += " ga('send','event','outgoing click',\"" + aInnerHtml + "\",\"" + v_gi_filename + "\"); }";
jscode += "}; ";
}
if (v_href[j].getAttribute('rel').indexOf('external') >= 0 ) {
jscode += " window.open(this.href); return false;";
}
}
if (jscode !== " ") { v_href[j].setAttribute('onclick',jscode); }
}
}
}
};
- Dans le code ci-dessus, la variable
v_gi_filename
est une variable globale et correspond à la page source. - Noter que le libellé du lien (
v_href[j].innerHTML
) est retraité par une expression régulière qui remplace les sauts de ligne, les tabulations et espaces en trop par un espace. - On en profite également pour ouvrir le lien dans un nouvel onglet si l’attribut
rel
a la propriétéexternal
.
Pour les téléchargements des documents PDFs, compte tenu du cahier des charges, le code devient :
for (j=0; j < v_href.length; j++) {
var jscode=" ";
if (v_href[j].getAttribute('rel') !== null) {
aInnerHtml = v_href[j].innerHTML.replace(/(\s{2,}|\t{1,})/g,' ').replace(/(\r\n|\n|\r)/g,'');
if ( v_href[j].getAttribute('rel').indexOf('nofollow') != -1 ) {
jscode += "if (typeof ga != 'undefined') { ";
jscode += " ga('send','event','outgoing click',\"" + aInnerHtml + "\",\"" + v_gi_filename + "\"); }";
jscode += "};";
}
else {
if ( v_href[j].getAttribute('href').indexOf('.pdf') >= 0) {
jscode += "if (typeof ga != 'undefined') { ";
jscode += " ga('send','event','pdf download',\"" + aInnerHtml + "\",\"" + v_gi_filename + "\"); ";
jscode += " ga('send','pageview'";
jscode += " ,{'page':\"" + v_href[j].getAttribute('href') +"\"";
jscode += " ,'title': \""+ aInnerHtml +"\"}" ;
jscode += " );" ;
jscode += "}; ";
}
}
if (jscode !== " ") { v_href[j].setAttribute('onclick',jscode); }
}
}
Le cahier des charges pour les scripts étant à peu près similaire aux téléchargements des documents PDFs, le code est ensuite retouché pour éliminer la redondance :
for (j=0; j < v_href.length; j++) {
var jscode=" ";
var v_event_category = false;
var v_pageview_page = false;
if (v_href[j].getAttribute('rel') !== null) {
aInnerHtml = v_href[j].innerHTML.replace(/(\s{2,}|\t{1,})/g,' ').replace(/(\r\n|\n|\r)/g,'');
if ( v_href[j].getAttribute('rel').indexOf('nofollow') != -1 ) {
v_event_category = "outgoing click";
}
else {
if ( v_href[j].getAttribute('href').indexOf('../scripts') >= 0 ) {
v_event_category = "script download";
v_pageview_page = v_href[j].getAttribute('href');
}
if ( v_href[j].getAttribute('href').indexOf('.pdf') >= 0) {
v_event_category = "pdf download";
v_pageview_page = v_href[j].getAttribute('href');
}
}
if (v_event_category) {
jscode += "if (typeof ga != 'undefined') { ";
jscode += " ga('send','event'";
jscode += " ,\"" + v_event_category + "\",\"" + aInnerHtml + "\",\"" + v_gi_filename + "\"); ";
jscode += "}; ";
}
if (v_pageview_page) {
jscode += "if (typeof ga != 'undefined') { ";
jscode += " ga('send','pageview'";
jscode += " ,{'page':\"" + v_pageview_page +"\"";
jscode += " ,'title': \""+ aInnerHtml +"\"}" ;
jscode += " );" ;
jscode += "}; ";
}
if (v_href[j].getAttribute('rel').indexOf('external') >= 0 ){
jscode += " window.open(this.href); return false;";
}
if (jscode !== " ") { v_href[j].setAttribute('onclick',jscode); }
}
}
Et c’est terminé. Il ne reste plus qu’à appeler cette fonction rebuild_href
en fin de traitement de la page et de l’arbre DOM sur les éléments HTML concernés (<section id="theme">
et <article>
pour cet exemple).
lib.js
rebuild_href = function() {
...
};
process_post_load = function() {
rebuild_href('article');
rebuild_href('theme');
};
if (window.addEventListener) {
window.addEventListener("load",process_post_load, false);
}
Le code Google Analytics est incorporé dynamiquement et automatiquement par le moteur Javascript du navigateur : plus aucun codage en dur ou code généré avec PHP. Si les API changent dans le futur, il y aura juste la fonction rebuild_href
à migrer.
Aller plus loin dans l’automatisation
L’attribut rel
des liens hypertextes, l’extension ou la localisation du fichier ont suffi pour automatiser l’implémentation du code Google Analytics. La définition des attributs personnalisés data-
(nouveauté HTML 5) dans les liens hypertextes offrent encore des fonctionnalités supplémentaires pour l’automatisation.
Un exemple : la liste des articles les plus visités et partagés obtenue avec les services AddThis (pour plus d’informations : Distribution de données inter domaines avec JSONP, cas pratiques (AddThis…)).
Cette liste est encapsulée dans un bloc section
avec l’id topten
et pour tous les liens dans cette liste, on souhaite déclencher un évènement particulier.
ga('send','event','addthis top10','<Titre>','<URL>');
Pour traiter ce contexte, chaque lien est créé avec un attribut data-afwk-addthistopten
:
<a href="http://www.sqlpac.com/referentiel/docs/oracle-gestion-verrous-locks.html"
rel="external" data-afwk-addthistopten="true">
Oracle et les verrous
</a>
La fonction rebuild_href
est adaptée pour traiter ce cas et appelée avec le paramètre topten
lorsque les données d’AddThis sont reçues et traitées :
rebuild_href = function(tagoridorclass) {
…
for (j=0; j < v_href.length; j++) {
if (v_href[j].getAttribute('rel') !== null) {
if (v_href[j].getAttribute('rel').indexOf('external') >= 0 ){
…
if (v_href[j].getAttribute('data-afwk-addthistopten') !== null) {
v_event_category = "addthis top10";
}
…
}
}
…
};
lib.js
display_addthistopten = function (data) {
// Affichage de la liste
for (i=0; i < data.length; i++) {
…
}
// Reconstruction des liens pour ajouter le code Google Analytics
rebuild_href('topten');
};
Le mode debug avec analytics.js
L’ancienne librairie ga.js
proposait un mode debug (Google Analytics et le mode debug (ga_debug.js)), bien pratique pour détecter pourquoi une action Google Analytics n’est pas réalisée. On s’en rend vite compte lorsque les statistiques chutent d’un seul coup.
La librairie analytics.js
offre cette toujours fonctionnalité. En mode debug, au lieu d’appeler la librairie analytics.js
, le script analytics_debug.js
est appelée plus un petit tableau {trace: true}
. Ne pas oublier de retirer le mode debug lorsque ce n’est plus nécessaire pour les performances.
lib.js
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics_debug.js','ga');
window.ga_debug = {trace: true};
ga('create', 'UA-XXXXXXX-X');
ga('send', 'pageview');
Comme pour ga.js
, tous les messages sont notifiés dans la console Javascript des outils de développement du navigateur. Pour Chrome : Ctrl + Maj + IConsole ou Plus d’outilsOutils de développementConsole.
_ _ _ _
| | | | | | (_)
__ _ ___ ___ __ _| | ___ __ _ _ __ __ _| |_ _| |_ _ ___ ___
/ _` |/ _ \ / _ \ / _` | |/ _ \ / _` | '_ \ / _` | | | | | __| |/ __/ __|
| (_| | (_) | (_) | (_| | | __/ | (_| | | | | (_| | | |_| | |_| | (__\__ \
\__, |\___/ \___/ \__, |_|\___| \__,_|_| |_|\__,_|_|\__, |\__|_|\___|___/
__/ | __/ | __/ |
|___/ |___/ |___/
analytics_debug.js:10 Running analytics_debug.js. This script is intended for testing and debugging only.
analytics_debug.js:10 Initializing Google Analytics.
analytics_debug.js:10 Analytics.js is secure, forcing SSL for all hits.
analytics_debug.js:10 Running command: ga("create", "UA-XXXXXXX-X", {siteSpeedSampleRate: 100})
analytics_debug.js:10 Creating new tracker: t0
...
Rien de bien nouveau dans le mode debug par rapport à l’ancienne librairie, sauf une meilleure lisibilité (arborescence hiérarchique en mode treeview) :
et le message Tracking beacon sent
n’a plus à être recherché, il n’existe plus d’ailleurs, les avertissements et erreurs sont désormais repérés en mode debug avec des icônes qui sautent vite aux yeux.
Conclusion
L’exploitation des attributs rel
et data-
des liens hypertextes facilite grandement l’automatisation des actions Google Analytics avec une fonction Javascript générique appelée en fin de chargement de l’arbre DOM.
Cette fonction rend le code beaucoup moins dépendant des futurs changements d’API de Google Analytics et la migration se résumera alors à modifier la fonction en 2 minutes.