Google Optimize not triggering when running on localhost - google-analytics

I am trying to get Google Optimize experiment data in JavaScript, by following these instructions. But I get no callback and I can't see anything happening in the debugger either.
The linked instructions use the gtag way of configuring GA, so I have set up gtag according to https://developers.google.com/gtagjs/devguide/snippet, and configured Optimize according to https://support.google.com/optimize/answer/7513085?hl=en with Method 1: Install Optimize with the global site tag (gtag.js).
I want to show the exact code I am using, but since I am doing it in React with Next.js Server Side Rendering, the original code has some extra stuff compared to plain HTML+JS. The source looks like this:
require("dotenv").config()
import React from "react"
import Document, { Head, Main, NextScript } from "next/document"
export default class extends Document {
render() {
return (
<html>
<Head>
<script async src={`https://www.googletagmanager.com/gtag/js?id=${process.env.GA_TRACKING_ID}`}/>
<script
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){ dataLayer.push(arguments); }
gtag('js', new Date());
gtag('config', '${process.env.GA_TRACKING_ID}', { optimize_id: '${process.env.OPTIMIZE_ID}'});
console.log('GA setup done: ${process.env.GA_TRACKING_ID} + ${process.env.OPTIMIZE_ID}');
gtag('event', 'optimize.callback', {
name: '${process.env.EXPERIMENT_ID}',
callback: o => console.log(o)
});
`
}}
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
)
}
}
This code produces a result page that starts Analytics and tracks a pageView just like it should, and when I do "RUN DIAGNOSTICS" in the Google Optimize console it opens the page, checks the JavaScript and reports:
Optimize is correctly installed
No major issues were detected while verifying the Optimize installation on this page.
I have also installed the Google Chrome Tag Assistant plug-in, and it reports that the Google Optimize tag is correctly installed.
I can see the following calls in the network log:
https://www.googletagmanager.com/gtag/js?id=UA-xxxxxx
https://www.google-analytics.com/analytics.js
https://www.google-analytics.com/gtm/js?id=GTM-xxxxxx&t=gtag_UA_xxxx&cid=nnn.nnn
I have also verified that the google_optimize global variable is created, and it has a .get() method. If I look (in the debugger network panel) at the http response of the https://www.google-analytics.com/gtm/js?... request I can actually see the Google Optimize experiments data with the correct Experiment embedded in the code.
So everything looks good, except for the fact that the optimize.callback event seems to be a complete no-op. It does not do anything at all. And I don't know how else to access the experiments data that I see in the http response in the debugger.

So I finally figured out what caused my struggles: I was running from a local NodeJS server, on the usual http://localhost:3000 url. Turns out Google Optimize will never trigger an experiment on http://localhost/... urls. The code wants a hostname part of the url that can be parsed according to a host.domain scheme.
In other words, there must be a dot in the hostname for Optimize to trigger an experiment.
For local testing, this can be fixed by creating a hostname alias such as localhost.domain for 127.0.0.1, and accessing the web page using this alias.

I found a way, which can achieve start google optimize experiment with localhost.
So, just use next settings for google optimize experiment and it will start successfully.

Related

How can I set a (Google Analytics) Content Experiment (A/B test) with a singular URL?

I want to test whether a variant of my current homepage will perform better (reach a certain goal defined on GA), but I'm struggling to find a good solution.
Let's say the current version (Original) has sections A, B, C in that order. The other version (Variant), though, has sections in the order B, C, A. Note: the top navigation menu (linking to each section's anchor) must reflect this change.
I first considered Client-Side Experiments, which I have done in the past, but I'm worried those wouldn't look good when rendering the site: depending on loading speed, a Variant user would first see section A, and then they would see section A vanish (into the bottom of the page), while they could also notice the menu being changed.
I then considered the regular Experiment mode on Analytics, which asks me to determine two URLs to compare: the Original is my root path (e.g. http://mywebsite.xyz/) and the Variant would be something like http://mywebsite.xyz/home. But I can't orientate half of my users to this different URL - I want to test people who reach my homepage from wherever they may come.
I tried something through the Google Cloud App Engine: publishing two versions of my application, and splitting traffic 50/50 between both (cookie-based). When visiting the root path, one renders the Original, while the other renders the Variant version. Great. It works! But now Google Analytics has no idea which version it's tracking...
What I did: when the page first loads, I send an event with the version identifier as parameter, such as
ga('send', 'event', 'AB_Test', 'A');
// or
ga('send', 'event', 'AB_Test', 'B');
I suppose these would help me check the Events Flow and determine which version reaches more Event goals, but it's still not linked to an Experiment or its features.
Is there any better way to implement this? Am I missing something? It feels too complicated to do something that both the Cloud App Engine and the Analytics should help me achieve. (Thanks for reading, I know it's a huge text)
When I run this type of experiment, I typically try to leverage the API to determine the variation in the backend and then render the template appropriately.
For your current workaround, rather than sending events, I'd recommend instantiating the test on each server and manually setting the variant for each. See code below:
<!-- App 1 / Original -->
<script type="text/javascript" src="//www.google-analytics.com/cx/api.js?experiment=<EXPERIMENT ID>"></script>
<script type="text/javascript">
(function () {
cxApi.setChosenVariation(0);
}());
</script>
<!-- App 2 / Variant -->
<script type="text/javascript" src="//www.google-analytics.com/cx/api.js?experiment=<EXPERIMENT ID>"></script>
<script type="text/javascript">
(function () {
cxApi.setChosenVariation(1);
}());
</script>

The Google Optimize snippet for the container with ID GTM-... is not correctly installed on this page

I'm trying to use Google Optimise with Analytics installed by gtag.js and am getting errors in some parts of the Optimise UI but not others.
Here's what I've done:
Installed Google Analytics following the instructions "Set up Analytics tracking with gtag.js" (and confirmed it's working by using the Real-Time Preview).
Set up Google Analytics following the instructions "Deploy Optimize using Global Site Tag (gtag.js)"
Added the page-hiding snippet.
When I set up an experiment using the Chrome extension, Optimise appears to be running as Google expect - notice that there's no number shown by the exclamation mark icon in the screenshot below (there is if I remove the Optimise code), and clicking it says "no issues detected". (Also, the chrome extension's icon doesn't have any error symbol.)
However, if I then preview either the original or the variant (clicking the "screen" icon in Optimise), I get a javascript alert which says:
The Google Optimize snippet for the container with ID GTM-abcdef is not correctly installed on this page. To preview variants for this experiment, make sure the Google Optimize snippet is installed on any pages you want to test.
(I've replaced my actual container ID by "abcdef" in the above.)
And the Chrome extension icon also shows an error and a similar message when clicked (I've blacked out the experiment name here):
Here's my HTML <head> section (with IDs redacted):
I've compared the script from this with the example in "Deploy Optimize using Global Site Tag (gtag.js)" and they appear identical except that my code contains real IDs:
<head>
<meta charset="UTF-8">
<!-- Page hiding snippet (recommended) -->
<style>.async-hide { opacity: 0 !important} </style>
<script>(function(a,s,y,n,c,h,i,d,e){s.className+=' '+y;h.start=1*new Date;
h.end=i=function(){s.className=s.className.replace(RegExp(' ?'+y),'')};
(a[n]=a[n]||[]).hide=h;setTimeout(function(){i();h.end=null},c);h.timeout=c;
})(window,document.documentElement,'async-hide','dataLayer',4000,
{'GTM-zyxzyx':true});</script>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-abcdef"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-abcdef', { 'optimize_id': 'GTM-zyxzyx'});
</script>
<title></title>
</head>
I've tried this against a site running locally (on Flask's webserver, port 5000) and against a site deployed onto Heroku, and behaviour is the same in both instances.
I've also tried removing the page-hiding snippet code, but that didn't help.
I've also tried actually running the experiment but always saw the original variant even when setting the alternative one to have 100% traffic, so it's not working there. (I only tried this against local site, haven't tried against the Heroku instance.)
It's worth noting that there's this line in the GA code:
function gtag(){dataLayer.push(arguments);}
I've seen it online in that form and with the semi-colon at the end:
function gtag(){dataLayer.push(arguments)};
I've tried both without any effect.
What am I doing wrong here?
I don't know if this is correct, but should the hiding snippet be using the ID UA-abcdef?
If you’re using Google Tag Manager to deploy Optimize, populate the
page-hiding snippet with your Tag Manager container ID, not your
Optimize container ID. Learn more about using Optimize with a
tag-management system (from here: https://support.google.com/optimize/answer/7100284?hl=en)
I'm not sure whether deploying the code this way is classed as deploying Optimize with Google Tag Manager though. What do others think?

Google closure base.js & modules

I'm trying to simply load google closure modules in my browser at development time, so I don't need any crazy advanced compilation.
my index.js contains:
goog.module("Widgets.index");
var Widgets$app = goog.require("Widgets.app");
/* rest of the code */
my index.html contains the following :
<script src="closure/base.js"></script>
<script src="index.js"></script>
I get the following in my console:
Module Widgets.index has been loaded incorrectly.
Note, modules cannot be loaded as normal scripts.
They require some kind of pre-processing step
How do I pre-process the index.js?
I just want to load some simple google modules in my browser, this is development time. No need for any crazy (slow?) optimizations..
Your entry point is included on the page as a script but defines itself as a module. That's what the error message is telling you. Instead:
index.js
goog.provide("Widgets.index");
var Widgets$app = goog.require("Widgets.app");
/* rest of the code */

Is google experiments API available in analytics.js?

We try to set up google experiments to work with our backend setup and found that there is API for letting GA know what variation we have selected to show for the user through function:
cxApi.setChosenVariation(chosenVariation, opt_experimentId);
When I visit official docs: https://developers.google.com/analytics/devguides/collection/gajs/experiments
it says that:
"ga.js is a legacy library. If you are starting a new implementation we recommend you use the latest version of this library, analytics.js. For exisiting implementations, learn how to migrate from ga.js to analytics.js."
We do use analytics.js.
Does it mean that all the functions present in ga.js are already in analytics.js and we do not need to worry about using this function?
You can try the browser-only Implementation as explained at https://developers.google.com/analytics/devguides/collection/analyticsjs/experiments. Instead of ga.js or analytics.js, you can try gtag.js. Visit https://developers.google.com/analytics/devguides/collection/gtagjs/migration to learn how to migrate from analytics.js to gtag.js. I am sharing the code below of how I implemented my experiment. Remember to create the experiment in Google Analytics, you will find it in the BEHAVIOR > Experiments section. In the code below, you will need to use your own Experiment ID.
<head>
.......................................
.......................................
.......................................
<!-- Load the Content Experiment JavaScript API client for the experiment -->
<script src="//www.google-analytics.com/cx/api.js?experiment=MY_EXPERIMENT_ID"></script>
<script>
// Ask Google Analytics which variation to show the user.
var chosenVariation = cxApi.chooseVariation();
// Define JavaScript for each page variation of this experiment.
// Wait for the DOM to load, then execute the view for the chosen variation.
$(document).ready(function(){
switch (chosenVariation) {
case 0:
// Original: Do nothing. This will render the default HTML.
break;
case 1:
//document.getElementsByClassName('logo_tagline')[0].value = 'I love programming';
$(".logo_tagline:first").text("I love programming");
break;
case 2:
//document.getElementsByClassName('logo_tagline')[0].value = 'Programming is my passion';
$(".logo_tagline:first").text("Programming is my passion");
break;
case 3:
//document.getElementsByClassName('logo_tagline')[0].value = 'I enjoy writing code';
$(".logo_tagline:first").text("I enjoy writing code");
}
});
</script>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-XXXXXXXXX-X"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-XXXXXXXXX-X');
</script>
</head>

Need to include a helper html page in my meteor project

I have a helper.html page that I need to include in my Meteor app to assist with using an api. Looks like this.
<head>
<script type="text/javascript" src="https://www.example.com/api-helper.js"> </script>
</head>
<body>
</body>
Right now I just have it in the root directory of my app. As it is now, here are the problems: the other api script which I have included in main.html, can't find the helper.html. Second, I get this error in the console: Error: Oh no! No route found for path: "/helper.html?. So my question is how do I properly include this helper html page into my Meteor project? Help greatly appreciated.
Thanks
Just put your html file into a folder called public in the root folder.
Error: Oh no! No route found for path: "/helper.html"
This is an error from iron:router, it's complaining that it can't find any route for path "helper.html".
I suppose you get this error message when typing directly http://localhost:3000/helper.html in your browser address bar, which is WRONG because this is not how iron:router is supposed to work.
iron:router manages pure client-side routing using HTML5 push state API contrary to classic server-side routing involved when requesting "/helper.html" to Apache or nginx means the server is going to send you an actual HTML response page displayed by the browser.
In Meteor "single-page apps", the server does not send any HTML responses to the client, it only sends data. It means that the routing takes place entirely in the client, the URL in the address bar gets parsed and iron:router provides utilities to respond accordingly, which usually involves rendering a different template based on which path (route) you hit.
I hope you really understand the difference in nature between these two approaches because this is very important to be aware of when developing with Meteor.
As far as your problem is concerned, I'll take DISQUS integration as an example which seems to be a similar issue.
DISQUS integration on standard PHP solutions is straightforward, you just need to copy-paste this so-called universal embed code :
<div id="disqus_thread"></div>
<script type="text/javascript">
/* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
var disqus_shortname = '<example>'; // Required - Replace example with your forum shortname
/* * * DON'T EDIT BELOW THIS LINE * * */
(function() {
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
</script>
<noscript>Please enable JavaScript to view the comments powered by Disqus.</noscript>
blog comments powered by <span class="logo-disqus">Disqus</span>
This is fine because once a page gets sent to the client in a traditional website, javascript code gets executed once and we are done : if the user click on another page on the website, the all process of generating the HTML response, sending it to the client and executing JS code will be started from scratch.
However we can't just copy-paste this code inside a Meteor template, because Meteor webapps are "single-page apps", no page reloading process should ever happen in these type of websites.
When we navigate to another page in a Meteor app, the javascript context stays the same and we don't want the DISQUS script to reexecute. What we need to do instead is loading the script only once in the rendered callback of our template.
So we come up with the following Meteor template logic to integrate DISQUS comments loading :
<template name="disqus">
<div id="disqus_thread"></div>
<a href="http://disqus.com" class="dsq-brlink">
blog comments powered by <span class="logo-disqus">Disqus</span>
</a>
</template>
Template.disqus.rendered=function(){
// assumes we get identifier and title as data context
var identifier=this.data.identifier;
var title=this.data.title;
// we need a way to tell if the script has already been loaded or if it's
// the first time, we can check for the existence of the DISQUS variable on
// the global window object to make the difference
if(window.DISQUS){
// DISQUS API has our back, see this resource
// https://help.disqus.com/customer/portal/articles/472107-using-disqus-on-ajax-sites
window.DISQUS.reset({
reload:true,
config:function(){
this.page.identifier=identifier;
this.page.title=title;
}
});
}
else{
// first initialization
window.disqus_shortname = "disqus-shortname";
window.disqus_identifier = identifier;
window.disqus_title = title;
window.disqus_developer = 1;
// load the script via JS instead of embedding it in the HTML
var script = $("<script>",{
type:"text/javascript",
async:true,
src:"//" + disqus_shortname + ".disqus.com/embed.js"
});
$("head").append(script);
}
};
This example demonstrates the way to go when you need to embed API code (google-analytics, DISQUS, etc...) in a Meteor website.

Resources