In the EU, there's a cookie law that requires you to load third-party scripts after the user expresses consent, by clicking, scrolling or navigating for instance. So I load 3 scripts by executing a function that's called after document loads. This is the code:
The problem is that it doesn't always work, nor always fail. I see sessions and activity but I also know for a fact that there are visits that don't trigger the scripts because when I tested it myself on several other computers, not all activity was saved in analytics.
What should I fix in the function to make it work all the time?
$(document).ajaxComplete(function() {
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-XXXXX-X']);
_gaq.push(['_trackPageview']);
var loadGoogleAnalytics = function(){
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
}
});
If you have a look at this post, you can modify it slightly to achieve what you want:
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-XXXXX-X']);
_gaq.push(['_trackPageview']);
var loadGoogleAnalytics = function(){
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
}
</script>
Then just call loadGoogleAnalytics() whenever the user agrees with your Cookie Usage display
Other response seems to be targeting ga.js which is legacy, whereas your question seems to be oriented towards analytics.js (current).
Studying the analytics.js snippet a little you can easily find an answer (below, formatted with prettier):
(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-XXXXX-Y", "auto");
ga("send", "pageview");
The trick is easy : Google creates a global var GoogleAnalyticsObject which is a string representing the name of the global GoogleAnalytics. You can see that by default it's "ga".
So, at loading you need to create two global vars : GoogleAnalyticsObject which is a string and window[GoogleAnalyticsObject] which is the function ( which just accumulates events, this function is replaced when the lib is loaded I guess ). Then when your user accepts the cookies you can load the lib and you're done :)
You can try use this function that works fine to me:
function loadGoogleAnalytics(trackingId) {
var deferred = $q.defer();
function loadScript() {
var scriptId = 'google-analytics';
if (document.getElementById(scriptId)) {
deferred.resolve();
return;
}
var s = document.createElement('script');
s.id = scriptId;
s.innerText = "(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','//www.google-analytics.com/analytics.js','ga');ga('create', '" + trackingId + "', 'auto');";
// most browsers
s.onload = initGA();
// IE 6 & 7
s.onreadystatechange = function () {
if (this.readyState == 'complete') {
initGA();
}
}
document.getElementsByTagName('head')[0].appendChild(s);
}
$window.initGA = function () {
deferred.resolve();
}
loadScript();
return deferred.promise;
}
Could you try adding the following instead of a call to .innerHTML()
//...text above...
TheAnalyticsScript.text = ['ga-script-string', 'fb-script.string','hotjar.string'].join('\n');
$('body').append(TheAnalyticsScript);
Please let me know if that works.
Here's my pick on the problem. The solution simply patches the default scripts to return function which then adds the script tag only after the consent is granted. Have a look:
// for GoogleAnalytics
var loadGA = (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();
// call this function after receiving consent
return function loadGA() {
a = s.createElement(o),
m = s.getElementsByTagName(o)[0];
a.async = 1;
a.src = g;
m.parentNode.insertBefore(a, m)
}
})(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');
// for Facebook
window.fbAsyncInit = function() {
FB.init({
appId: 'your-app-id',
autoLogAppEvents: true,
xfbml: true,
version: 'v2.10'
});
FB.AppEvents.logPageView();
};
var loadFB = (function(d, s, id) {
//- call this function after receiving consent
return function loadFB() {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {
return;
}
js = d.createElement(s);
js.id = id;
js.src = "//connect.facebook.net/en_US/sdk.js";
fjs.parentNode.insertBefore(js, fjs);
}
}(document, 'script', 'facebook-jssdk'));
// for HotJar
var loadHJ = (function(h, o, t, j, a, r) {
h.hj = h.hj || function() {
(h.hj.q = h.hj.q || []).push(arguments)
};
h._hjSettings = {
hjid: 1,
hjsv: 5
};
return function loadHJ() {
a = o.getElementsByTagName('head')[0];
r = o.createElement('script');
r.async = 1;
r.src = t + h._hjSettings.hjid + j + h._hjSettings.hjsv;
a.appendChild(r);
}
})(window, document, '//static.hotjar.com/c/hotjar-', '.js?sv=');
// finally when you receive the consent
function onConsentReceived() {
loadGA();
loadFB();
loadHJ();
}
//- meanwhile you can continue using ga(), hj() whatever here...
Each of the loader can be in it's own script tag but make sure about the ordering of those tags.
Rather than appending the entire analytics script, you could have it included always but remove the following line:
ga("send", "pageview");
Then, when the user accepts the cookies.. fire the analytics function that sends the beacon using that same line above. Analytics does not actually send any data unless being told to specifically. That would be the most logical way of doing what you are attempting.
Thinking about what your code does, this function:
Creates a <script> element filled with strings
Adds it to the <head>
Note that the <head> is afaik supposed to be loaded only at the initial load. Depending on the browser implementation etc it might be the case that some of your visitor's browsers ignore the <script> tag you add after the page load is completed.
If I understand when you want to do right you want:
To load the page without the tracking scripts
Show a consent modal/popup
On 'agree' click trigger this function without a page reload
note: many EU websites reload the page after cookie consent because they add the <script> tags with tracking to the page using backend rendering. Often clicking 'I agree to cookies' they store a agreed: true type cookie that changes what webpage their server sends.
If you don't care about reloads:
Set a cookie when the user agrees
Reload the page
Have your backend (php/node/ruby/whatever) add a script tag based on this cookie
If you do care
Change your function to run the tracking codes instead of adding them to the DOM.
Just look at the tracking codes and break them down into easy to read code. For example the Ga code is actually simply:
var ga = document.createElement('script')
ga.type = 'text/javascript'
ga.async = true
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js'
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
And then
function addTrackers() {
// Execute the tracking things instead of adding them to the DOM
}
I have the following code in a script tag in my HTML page:
window.fbAsyncInit = function () {
if (FB != null) {
FB.init({
appId: 'myAppID',
status: true,
cookie: true,
xfbml: true,
oauth: true
});
FB.getLoginStatus(function (response) {
console.log("callback");
}, true);
}
};
(function () {
var e = document.createElement('script'); e.async = true;
var protocol = "http:";
if (document.location.protocol == "https:")
protocol = "https:";
e.src = protocol + '//connect.facebook.net/en_US/all.js';
document.getElementById('fb-root').appendChild(e);
}());
The page is loaded in an Android WebView from a domain matching the registered one in FB's app settings.
For no apparent reason, the callback no longer fires. It worked just yesterday, but today it doesn't work, without me changing any code or app setting.
I placed a console.log() call before the getLoginStatus call, and it seems that the call itself is reached and executed; also I can see that some files are loaded from static.ak.facebook.com. It's just that the callback isn't called.
The application is not in a Sandboxed mode, which seemed to have caused this behavior for others.
I should also mention that the same code works perfectly in a standalone browser (desktop & mobile).
What gives?
I have a simple site, I want to code that when ever a user on my site Like my Page which is embeded in it another button should appear below it saying "Proceed to Next Step"
Hope so you got my idea.
So, in essence, you have to use the FB API to achieve what you want. Here is the example:
$(document).ready(function()
{
window.fbAsyncInit = function() {
FB.init({ status: true, cookie: true, xfbml: true });
FB.Event.subscribe('edge.create', function(href, widget)
{
//Liked event - do something here
$('#something').show();
});
};
(function() {
var e = document.createElement('script');
e.type = 'text/javascript';
e.src = document.location.protocol + '//connect.facebook.net/en_US/all.js';
e.async = true;
document.getElementById('fb-root').appendChild(e);
}());
});
I was reading facebook javascript SDK when I saw the following syntaxt that I don't understand:
(function(){...}());
Does anyone know what does it do?
<div id="fb-root"></div>
<script>
window.fbAsyncInit = function() {
FB.init({appId: 'your app id', status: true, cookie: true,
xfbml: true});
};
(function() {
var e = document.createElement('script'); e.async = true;
e.src = document.location.protocol +
'//connect.facebook.net/en_US/all.js';
document.getElementById('fb-root').appendChild(e);
}());
</script>
it's an anonymous function, that gets called immediately after its declaration. It's used to create local variables without clobbering up the global scope. the variable e will not be visible nor accessible outside the function block
I'm trying to switch over to the new Facebook JavaScript SDK from the old JavaScript library (where I was using Connect) and I'm unable to get the permission dialog to appear. I'm trying to get the permission dialog that's shown under the "Data Permissions" section here.
My old call was this (with appropriate initialization):
FB.Connect.showPermissionDialog('publish_stream');
I tried changing to this (again, with appropriate initialization) but it's not working, it runs without error but the callback is never run:
FB.login(function(response)
{
if (response.session)
{
if (response.perms)
{
alert('user is logged in and granted some permissions: ' + response.perms);
}
else
{
alert('logged in but didnt grant permissions');
}
}
else
{
alert('not logged in');
}
},
{perms:'publish_stream'});
This is the initialization code I'm using:
window.fbAsyncInit = function()
{
FB.init({appId: 'xxxxxxxx', status: true, cookie: true, xfbml: true});
};
(function()
{
var e = document.createElement('script');
e.async = true;
e.src = document.location.protocol +
'//connect.facebook.net/en_US/all.js';
document.getElementById('fb-root').appendChild(e);
}());
After MUCH tinkering around with the code trying to get this to work I finally found out where the error was. The JavaScript code needs to be place at the end of the HTML, near the </body> tag. I moved it there and everything worked!