The setup
I have implemented an "onclick" method which calls a bit of third party code asynchronously. It provides a callback, and they suggest using a simple function that sets document.location = href
to make sure the page only changes after their method has returned. This works fine if you want to open the link in the same tab, but if you want to open in a new tab then one of two things happens:
The problems
The questions
Is it possible to have the callback function return true to the onclick event (in effect, making the entire chain of events synchronous)? Or, can you think of a way to preserve standard browser behavior in Case 1 above?
The code
https://developers.google.com/analytics/devguides/collection/analyticsjs/enhanced-ecommerce:
<a href="/product_details?id=P12345" onclick="clicked(); return !ga.loaded;">Product Link</a>
<script>
// Called when a link to a product is clicked.
function clicked() {
ga('ec:addProduct', {
'id': 'P12345',
'name': 'Android Warhol T-Shirt',
'category': 'Apparel',
'brand': 'Google',
'variant': 'black',
'position': 1
});
ga('ec:setAction', 'click', {list: 'Search Results'});
// Send click with an event, then send user to product page.
ga('send', 'event', 'UX', 'click', 'Results', {
'hitCallback': function() {
document.location = '/product_details?id=P12345';
}
});
}
</script>
This is a tricky issue that I've had to tackle multiple times in my career. I've taken various approaches, and in this answer I'll try to summarize my latest thinking:
Don't prevent default.
When you prevent the browser from taking its default action you end up having to recode that stuff yourself, and it gets pretty hairy to manually handle all the possible combinations of user intent and browser/platform variations. It's not worth it. There are some things (like checking which keys are pressed) that can be accounted for, but there are other things (like did the user right click and choose "open in a new tab") that are essentially impossible to detect.
Personally, I think it's best to just let the browser do its thing, and figure out another way to accomplish what you're trying to do.
Make the ga()
function sync.
In the sample code you provide, you're trying to send e-commerce and event hits to Google Analytics when the user clicks on a link. The problem is that the ga()
call is async, so if the user clicks on a link, the call may or may not finish before the browser loads the new page.
One possible solution would be to override how analytics.js is sending the hit to make it sync instead of async. This will allow you to send the hit without having to prevent default on the event and deal with opening the link manually.
analytics.js recently introduced Tasks, which allow you to override or modify various built-in behaviors of the library. Here's an example of how you'd override the sendHitTask
to implement a sync version:
ga('create', 'UA-XXXX-Y', 'auto');
ga(function(tracker) {
// Grab a reference to the default sendHitTask function.
var originalSendHitTask = tracker.get('sendHitTask');
// Modify the send hit task to be sync in cases where an
// external link was clicked.
tracker.set('sendHitTask', function(model) {
if (wasExternalLinkClicked()) {
var xhr = new XMLHttpRequest();
var url = '//www.google-analytics.com/collect?' + model.get('hitPayload');
// Specifying `false` as the third parameter makes the request sync.
xhr.open('GET', url, false);
xhr.send();
} else {
originalSendHitTask(model);
}
});
});
ga('send', 'pageview');
Beware, I haven't tested this code, but I think it should work. And, in case it's not obvious, you'll have to implement wasExternalLinkClicked()
yourself, but that shouldn't be too difficult.
Anyway, hopefully that help!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With