How to track custom events - javascript

I have a form that has multiple drop-downs and a submit button. This form posts via ajax and returns a different result depending on the combination selected in the dropdowns. Assuming I get a desired result, I want to be able to track this combination in analytics.
I would have expected to be able to use a javascript snippet that would allow me to push the custom event, along with the custom parameters, when the ajax result has come back. However I'm finding it very difficult to understand how to do this.
I've taken a look at the GA4 / Tag Manager documentation and I can see how to create the event and parameters, but when it comes to triggers I'm not sure what to do? GA provides the option to create a button click trigger, but I don't want to capture this data when the button is clicked - I only want to capture it when the response is received from the server. Ideally I'd do something like this:
$.post('/my_endpoint',
{
dropdown1: dropdown1_value
dropdown2: dropdown2_value,
dropdown3: dropdown3_value,
dropdown4: dropdown4_value
})
.done(function (response) {
if(<my_condition>) {
// Push to GA with dropdown1_value, dropdown2_value, dropdown3_value, dropdown4_value
}
});
Is there no way via script to push this event with the required parameters? You can see I require the ability to push the event manually, rather than relying on GA's triggers.
I recall in the past I've used dataLayer.push(), however this was years ago and analytics has evolved since I've last used it. I guess I'm trying to find Google Analytics APIs for pushing custom events, or does such a thing no longer exist?

You can do a dataLayer.push() of your parameters and an event like following:
dataLayer.push({'event': 'your-event-name', 'dropdown1_value' : value1, 'dropdown2_value' : value2, ...});
So in Google Tag Manager you have to create a dataLayer variable for how many values of dropdwon you want to pass. Then you have to create a GA4 Tag with the name you want, you enter the parameters with the values you retrieve from the dataLayer variables and as trigger you use an event named 'your-event-name'.

Related

what does method mean/do in gtag event?

I'm reading the google analytics documentation and I'm slightly confused about what method means. In their documentation they have
gtag('event', 'login', {'method': 'Google'});
https://developers.google.com/analytics/devguides/collection/gtagjs/sending-data
When you go to the measure Analytics page, method isn't listed in. They list the following params for event
gtag('event', <action>, {
'event_category': <category>,
'event_label': <label>,
'value': <value>
});
https://developers.google.com/analytics/devguides/collection/gtagjs/events
which doesn't list method. Does anyone know what this param does? And is it required to track events?
The method parameter is specific to the recommended login event. It tells you which method a user used to log in, for example credentials or google-oauth. Hence, it's not required (or applicable) to track GA4 events in general and it's not required (but recommended) for the login event.
Name
Type
Required
Example value
Description
method
string
No
Google
The method used to login.
(https://developers.google.com/tag-platform/gtagjs/reference/events#login)
In GA-UA you could control the values for Category, Action, Label (the row data) (plus custom dimensions).
Example:
Category
Action
Label
Interaction
Login
Google
In GA4, you can basically control the row data and the column names. (Gray fields can be set to any value).
Example:
Event Name
method
failed_attempts
...
login
Interaction
2
...
method is one of Google's recommended parameters, but you could also leave it out, give it a different name or add other parameters that you deem useful.

Tabulator Ajax Response Blocked - setFilter

I have Tabulator nearly working as I need for a web application I am designing. This app is calling web services in a backend app written in Java.
I created an InitialFilter set, the filtering, sorting, and pagination is handled by the backend. Next, I am creating an Accordion control for the various filter inputs by the end-user. No issues yet. I created two buttons, one to Apply the filter based on the user preferences, and another to Reset/Clear the filter parameters.
The Tabulator object is already created and has the default data already showing on the page. When the user sets the custom filter and clicks the Apply button, a JQuery function captures the on-click event and executes the following code.
$(function(){
$('#btn-apply').on('click', function(e){
// handle click event of button
// Get values first
var subFrom = $('#txt-submission-from').val();
var subTo = $('#txt-submission-to').val();
// Set filters
NIBRSTable.clearFilter();
NIBRSTable.addFilter("submissionPeriod", ">=", subFrom);
NIBRSTable.addFilter("submissionPeriod", "<=", subTo);
// Call function to load data
NIBRSTable.setData();
});
});
Error Returned
Ajax Response Blocked - An active ajax request was blocked by an
attempt to change table data while the request was being made
tabulator.min.js:5:24222
I have tried commenting out one source line at a time. It appears the setFilter() calls are causing the Ajax Response Blocked error even though there is not anything actively occurring (the tabulator DOM is already loaded)
I have many more items for which the end-user may filter. The two filters shown in the code listing above are just a start.
That isn't an error message, that is just a console warning.
What it means is that multiple ajax requests have been made in quick succession and that one request has been made before the first one returned, therefore the response of the first request will be ignored so the table isn't partially redrawn.
In this case it is being triggered because you are calling the addFilter function twice in quick succession which is triggering the ajax request twice with the second filter being added before the first ajax request has been sent. (there is also no need to call the setData function, adding a filter when ajaxFiltering is enabled will automatically trigger the request).
To avoid this double ajax request you could pass an array of filter objects into the addFilter function and only call it once:
NIBRSTable.addFilter([
{
field:"submissionPeriod",
type:">=",
value:subFrom
},
{
field:"submissionPeriod",
type:"<=",
value:subTo
},
]);
Oli,
Thank you for the detailed response. Since the filters are dynamic and set by the end-user (i.e. cannot be hardcoded), I created an Object and conditionally adding the filter parameters. Using this object, I can call the NIBRSTable.addFilter(userFilter) and it works like a charm! I did make the mistake of trying to JSON Stringify the object and passing it to the addFilter method, but quickly learned JSON Stringify was unnecessary since the object array was already a JSON object.
Oddly, though I am still receiving a single warning "Ajax Response Blocked" even though there were no pending Ajax actions. I only have one .addFilter() and removed the .setData() as you responded. I will ignore for now since the filtering is working!
Ben

Google Tag Manager Virtual Page View

I have a little problem setting up a virtualPageView which should override the URL which is sent to google when no result is present.
Heres what I have as JavaScript code:
function returnNoSearchResultsGoogleTagManagerCode($searchterm){
if ($searchterm == "") return "";
$requestUri = $_SERVER['REQUEST_URI'] . "&no_result=".$searchterm;
$js = "<script>
$(document).ready(function(){
dataLayer.push({
'event':'empty_result',
'virtualPageURL':'".$requestUri."'
});
});
</script>";
return $js;
}
As you can see, I want to use an event trigger (empty_result).
In google, I use a Trigger to determine if the Page is a no result Page. First i created a custom Variable with custom JS
function(){
if (document.getElementsByClassName('ga-no-result').length > 0){
return true;
}else{
return false
}
}
The class is set, if the SearchEngine can't find a result. So far so good.
I also created a dataLayer variable to hold the virtualPageURL
Now I need an event which is triggered if the variable is true.
Finally I created a Tag with type PageView which is fired when the event occurs:
Until now it seems okay, the Tag is properly configured (i guess) but if I do a search which has no result, the Page URL is not overridden
The Tag is properly fired and the variables are filled. The overview of the dataLayer shows a correct dataLayer event.
But the PageURL is not overridden... Even if I wait a whole day, the category isn't sent to google.
What am I doing wrong?
I would be very thankful if someone would have an idea or even a solution :)
Thanks in advance
exa.byte
UPDATE:
Hey, I forgot to mention, that I want to use the new page variable as the string which google should use to determine the searchterm and the searchcategory
In Google Analytics I configuered the search as the "q" parameter and the "no_result" as the category.
Is it even possible to change the string which google will parse in the end?
To send a virtual pageview to Google Analytics, the field you need to change is page not {{Page Url}} , also the title field is often used.
That's really the only two things you need to do to send a simple virtual pageview.
Extra: I always start my pagepath with /virtual/ to be able to recognize which ones are virtual pageviews easily in GA
For virtual page view you have to change Field "page" - in your GTM-OnSearchEmptyResult you are changing "{{Page URL}}" - I don't think that's correct way to send virutal pageview. Also if you need to change hostname use Fieldname "hostname".
In preview mode you will not see Page URL changed in Variables Tab, you have to go to the actual GA tag that is fired and check it's values. You can either do this in GTM's preview tool or you can use standard developer tools - Network Tab and see what values are being sent to GA:
You can see "dl" parameter is the current page, if you set up virtual page you should also see parameter called "dp" this is going to be the new value of page in your GA.
If you want to setup virtual pageview you have to use page instead of {{Page URL}} in your fieldname and for Document title use title in you fieldname.
for more field reference of google analytics follow below link
https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#hitType.
If you don't want to mess around with custom Tag Manager events it's still possible to use the good old ga method, even if you don't include the Analytics code on the page. You just need to fetch the right tracker dynamically, as explained by Simo Ahava in this thread.
if (typeof ga === "function") {
ga.getAll().forEach((tracker) => {
tracker.set('page', '/my/path'); // <- change here
tracker.send('pageview');
});
}
I also put it in a gist here.
thanks for your help. I think I got rid of the Problem and have solved it now. I will describe my solution below:
The solution is quite simple.
I had an error/ spelling error # google backend. I set the search_category parameter to "no_results", but used "no_result" for implementation...
Pretty dumb, but sometimes you just won't see the wood for the trees...
I created a new Trigger as helper "HelperDomReady" to trigger the only if DOM is ready and the variable "isEmptySearch" equals "(bool)true"
Now I can see the searchterms which have no result in google backend in the "sitesearch categories" summary. Since I won't set the parameter at all, if the search had at least one hit, the site-search category shows "not-set" for successful results. Therfore the category-section will only show searches without a hit. Problem solved :)
Disadvantage: The searchterm is also listed in the normal list. But I think this is negligible

Does Google Tag Manager "subscribe" to dataLayer.push? If yes, how?

When I push anything to datalayer (which is an array), does GTM notice that and push it directly to the Google Platform?
Or is it only happening once, when the GTM container is loaded?
If not, does GTM somehow subscribe to datalayer.push() ? Or is it just polling?
When you initialize GTM you pass the reference of you dataLayer object, which from that point on is replaced by an 'instance' of a special object managed by the GTM code. Compare what the chrome console tells you about the push method before and after you initialize GTM:
Before:
After:
When you push something to a GTM-initalized dataLayer, are actually calling a custom push method that informs it to the GTM code running on the client, if that push triggers a tag fire, it will result in a tag code being run, but it does not make any requests otherwise and is not sent to GTM servers or anything like that.
Observe that apart from pushes, there are many other events that GTM watches, such as clicks, form submits, DOM ready and even the container load, as you described.
You are not restricted to pushing events, you can (and in many cases should) push regular data (messages) and it will still be usable and visible on Google Tag Manager, just bear in mind that it needs an interaction to actually update its internal state (such as a click)
How they go about doing it, I do not know, but it could be done with something as simple as:
var dataLayer = [{name: 'Rui'}];
var DLWatch = (function(){
var init = function(){
window.dataLayer.push = push;
};
var push = function(data) {
window.dataLayer = window.dataLayer.concat([data]);
console.log('Data pushed!');
};
return {init: init};
})();
DLWatch.init();
dataLayer.push({foo: 'bar'});
To what you will get:
Data pushed!
And dataLayer will be
Here's my take on the DL. When you push something to it, GTM does detect it but it doesn't necessarily track that new data into GA. Data that is pushed in registers with the DL, but if you do not explicitly use that data in a pageview or an event or some other tag, then that data just remains in the DL. But to explicitly use the data you just pushed in, you should always also push an event which you can use to 'access' the new DL data.
So GTM does constantly 'listen' for new things being pushed in, but tags need to be set up to use the new data.

How do I catch an invalid API key for google maps

I have this code:
<script src="http://maps.google.com/maps?file=api&v=2&sensor=false&key=babab" type='text/javascript'></script>
If the key is invalid then it pops up an alert, but I want to perform some action in this case. I'm not sure how to hook into it though. Any ideas?
Google does not offer an external method of checking the Google Maps API key. Hence you cannot query some service with e.g. "Is this code valid abcde1234" and get a TRUE/FALSE response.
There is a discussion on how the Maps API key is generated. But I suggest you look at a post from Mike Williams about the GValidateKey function. This is the function actually doing the magic validation - what it exactly does, like creating a hash from your Google account / domain - we don't know.
I see two solutions for your problem of checking whether the API key provided is correct:
Overwrite the incoming alert with some custom code (check for the content of the alert, or check if an alert occurs withing X seconds after page load)
Somehow get the GValidateKey function to validate your key beforehand. Maybe you can call it before referencing the API Javascript? Sounds kind of hackish to me...
The problem you will likely have is that you don't know what Google actually checks. The referrer, the referring site, the host - many possibilities (it is not the IP address of the server, but the name plus some additional information).
I just ran across the need to perform an action if an invalid API key was used. Google's documentation states:
If you want to programmatically detect an authentication failure (for example to automatically send an beacon) you can prepare a callback function. If the following global function is defined it will be called when the authentication fails.
This was all I needed to do:
function gm_authFailure() { // Perform action(s) }
For modern browsers (IE9+ and others) you may use DOMNodeRemoved event. You just need to add event handler to the element that you pass to the map constructor:
var map = new google.maps.Map(element, myOptions);
element.addEventListener("DOMNodeRemoved", function(e){
if (e.target === element){
//your code here
element.removeEventListener("DOMNodeRemoved", mapWasRemovedHandler, true);
}
}, false);

Categories

Resources