How to retreive dataLayer value in GA correctly - google-analytics

I'm facing a problem while retreiving an event for GA. In GA, the eventLabel seems to find any problem with my dataLayer variables, as some of them are read as "undefined":
I've been researching my variables in code, but they seemed to be fine. And suddenly I found this while inspecting my GTM values:
The tag field was written in two lines and apparently "broken"! I supposed it was because of some sort of problem with my viewer, but I'm wondering if maybe that's the reason why my GA values are broken...
I checked length, and, when Iread Google documentation, the "eventLabel" tag can be 500 Bytes long:
And my texts are less than 500 Bytes long... So, what's the problem with my GA values? Is this "broken string" problem the responsible of the incorrect read of values in GA?
EDIT: This is the javascript behind.
The push:
dataLayer.push({
'event':'searchHome',
'eventCategory':'Home',
'eventAction':'search',
'eventLabel': 'route|departureDate|returnDate|category|quantity|variant|people|' + resident + '|' + vehicle,
'eventValue':'0'
});
Javascript to get the resident and vehicle variables (as they are the ones failing):
var vehicle = getTurismoValue($("#search-cars").val(), $("#search-cars option:selected").text(), $("#search-trailer").val());
var resident = getResidenciaValue($("#search-resident").val());
The functions behind:
function getTurismoValue(vehicle, literal, trailer) {
try {
if(vehicle != null && typeof vehicle !== "undefined") {
if (vehicle.trim().length > 0) {
return "Turismo|" + ((trailer.length > 0)?"si":"no");
}
}
return "0";
} catch (err) {
return "0";
}
};
function getResidenciaValue(resident) {
try{
if(resident != null && typeof resident !== "undefined") {
if (resident.trim().length > 0) {
return "peninsula";
}
return "extranjero";
}
return "0";
} catch(err) {
return "0";
}
};
Does anybody see anything weird with this javascript? What's failing?

Related

dataLayer variable isn't dynamically changing inside GTM custom HTML tag

<script>
console.log({{bounce_checker}});
(function(){
window.addEventListener('beforeunload',checker, false);})();
function checker(event){
event.preventDefault();
console.log(event.type);
console.log({{bounce_checker}});
var validator = {{bounce_checker}};
if(validator == "value_lead")
{console.log('value_lead');}
else if(validator == undefined)
{console.log('bounced_user');}
</script>
The above code is deployed as custom HTML tag in DOM ready event. At the time when it fires {{bounce_checker}} dataLayer variable will be undefined but it's setup to change after a user interaction. When 'beforeunload' event happens, the {{bounce_checker}} is to display 'value_lead' if there was any user interaction else 'bounced_user'.
But the data Layer variable isn't dynamically changing. its always showing up as 'undefined' just the way it was during the DOM event.
Can anyone please tell me why this is happening?
with this code, you add a listener that utilizes the value of {{bounce checker}} ar the moment when the listener was created e.g. undefined. GTM will not update that variable since it's not a regular js object but an expression that evaluates to some value.
Consider switching to pushing some event to dataLayer on 'beforeunload' and processing your {{bounce checker}} value at custom HTML tag triggered by custom event, something like:
<!--DOM Ready Custom HTML Tag code -->
<script>
window.addEventListener('beforeunload',function(e) {dataLayer.push({'event':'beforeunload'})}, false);
</script>
<!--beforeunload Custom Event Custom HTML Tag code -->
<script>
console.log({{bounce_checker}});
var validator = {{bounce_checker}};
if(validator == "value_lead") {
console.log('value_lead');
// further processing of {{bounce_checker}} value
// ...
}
else if(validator == undefined) {
console.log('bounced_user');
// further processing of {{bounce_checker}} value
// ...
}
</script>
Thank you #Дмитро Булах. That makes sense. I also had other workarounds which solved my issue like manually parsing through the dataLayer using for...loop or forEach loop.
(function() {
window.addEventListener('beforeunload', checker, false);
})();
function checker(event) {
console.log(event.type);
console.log({
{
bounce_checker
}
});
var i;
var arr = window.dataLayer;
for (i = 0; i <= arr.length; i++) {
if (typeof(arr[i]) === 'undefined') {
console.log('caught error')
} else if (arr[i].bounced_user) {
var validator = arr[i].bounced_user;
} else {}
};
console.log({
{
bounce_checker
}
});
console.log(validator);
if (validator == "value_lead") {
console.log('value_lead');
} else if (validator == undefined) {
console.log('bounced_user');
}
console.log('testing success');
}
Also, in GTM community forums, Simo Ahava had suggested me to use
var validator = window.google_tag_manager[{{container_ID}}].dataLayer.get('bounce_checker');

How to make TagsInput to work with both auto complete & free text

UPDATE
This issue is already discussed in github here
I am using tagsinput with typeahead in bootstrap 3. The problem which I am experiencing is with the value in case if user selects the existing tag. Display text shows it right but .val() returns its actual object. Below is the code
$('#tags').tagsinput({
//itemValue: 'value',
typeahead: {
source: function (query) {
//tags = [];
//map = {};
return $.getJSON('VirtualRoomService.asmx/GetTags?pid=' + $("#<%=hdnPID.ClientID%>").val() + '&tok=' + query)
//, function (data) {
// $.each(data, function (i, tag) {
// map[tag.TagValue] = tag;
// tags.push(tag.TagValue);
// });
// return process(tags);
//});
}
}
//freeElementSelector: "#freeTexts"
});
The problem with above code is that it results as below while fetching tags from web method
This happens when user select the existing tag. New tags no issues. I tried setting itemValue & itemText of tagsinput but not worked. Hence I decided a work-around of this problem. Since I could able get the json string as ['IRDAI", Object], if can somehow parse these object & get the actual tag value then I get the expected result of the code I am looking at.
Below is what it appears in tags input as [object Object] for text selected by user from auto populated drop down
[![enter imt
If I i specify TagId & TagValue to itemValue & itemText as below code
$('#tags').tagsinput({
itemValue: 'TagId',
itemText: 'TagValue',
typeahead: {
source: function (query) {
//tags = [];
//map = {};
return $.getJSON('VirtualRoomService.asmx/GetTags?pid=' + $("#<%=hdnPID.ClientID%>").val() + '&tok=' + query)
//, function (data) {
// $.each(data, function (i, tag) {
// //map[tag.TagValue] = tag;
// tags.push(tag.TagValue);
// });
//});
// return process(tags);
}
}
//freeElementSelector: "#freeTexts"
});
Then the result is displaying as below when below code is executed
var arr = junit.Tags.split(',');
for (var i = 0; i < arr.length; i++) {
$('#tags').tagsinput('add', arr[i]);
}
Given your example JSON response from your data source:
[
{"TagId":"1", "TagValue":"eSign"},
{"TagId":"2", "TagValue":"eInsurance Account"}
]
You'll need to tell tagsinput how to map the attributes from your response objects using itemValue and itemText in your tagsinput config object. It looks like you may have started down that path, but didn't reach the conclusion, which should look something like:
$('#tags').tagsinput({
itemValue: 'TagId',
itemText: 'TagValue',
typeahead: {
source: function (query) {
return $.getJSON('VirtualRoomService.asmx/GetTags?pid=' + $("#<%=hdnPID.ClientID%>").val() + '&tok=' + query);
}
}
});
Be sure to checkout the tagsinput examples.
This may not be the clean solution but I got around this issue through below parsing method. Hope this helps someone.
var items = $('#tags').tagsinput("items");
var tags = '';
for(i = 0; i < items.length; i++)
{
if(JSON.stringify(items[i]).indexOf('{') >= 0) {
tags += items[i].TagValue;
tags += ',';
} else {
tags += items[i];
tags += ',';
}
}

Open multiple links in casperjs

I am trying to scrape all links of special kind (boxscore-links) from this website http://www.basketball-reference.com/teams/GSW/2016_games.html and then visit them one by one, scraping some information from every visited link. For a beginning I want to scrape all links, visit them one by one and get a title of website. The problem is that it always prints the same title and the same current url (initial url) even though it clearly has to be a new one. Seems to me that there is a problem with 'this'-keyword...
(Don't look at limit of links, I took the code from sample on github of casperjs and I left it for console not to be overloaded.)
This is my code:
var casper = require("casper").create({
verbose: true
});
// The base links array
var links = [ "http://www.basketball-reference.com/teams/GSW/2016_games.html" ];
// If we don't set a limit, it could go on forever
var upTo = ~~casper.cli.get(0) || 10;
var currentLink = 0;
// Get the links, and add them to the links array
function addLinks(link) {
this.then(function() {
var found = this.evaluate(searchLinks);
this.echo(found.length + " links found on " + link);
links = links.concat(found);
});
}
// Fetch all <a> elements from the page and return
// the ones which contains a href starting with 'http://'
function searchLinks() {
var links = document.querySelectorAll('#teams_games td:nth-child(5) a');
return Array.prototype.map.call(links, function(e) {
return e.getAttribute('href');
});
}
// Just opens the page and prints the title
function start(link) {
this.start(link, function() {
this.wait(5000, function() {
this.echo('Page title: ' + this.getTitle());
this.echo('Current url: ' + this.getCurrentUrl());
});
});
}
// As long as it has a next link, and is under the maximum limit, will keep running
function check() {
if (links[currentLink] && currentLink < upTo) {
this.echo('--- Link ' + currentLink + ' ---');
start.call(this, links[currentLink]);
addLinks.call(this, links[currentLink]);
currentLink++;
this.run(check);
} else {
this.echo("All done.");
this.exit();
}
}
casper.start().then(function() {
this.echo("Starting");
});
casper.run(check);
Considering an array of URLs, you can iterate over them, visiting each in succession with something like the following:
casper.each(urls, function(self, url) {
self.thenOpen(url, function(){
this.echo('Opening: ' + url);
// Do Whatever
});
});
Obviously this will not find links on a page, but it is a nice way to go over a known set of URLs.

Filter results from Google Autocomplete

Is there a way to get the results from Google Autocomplete API before it's displayed below the input? I want to show results from any country except U.S.A.
I found this question: Google Maps API V3 - Anyway to retrieve Autocomplete results instead of dropdown rendering it? but it's not useful, because the method getQueryPredictions only returns 5 elements.
This is an example with UK and US Results: http://jsfiddle.net/LVdBK/
Is it possible?
I used the jquery autocomplete widget and called the google methods manually.
For our case, we only wanted to show addresses in Michigan, US.
Since Google doesn't allow filtering out responses to that degree you have to do it manually.
Override the source function of the jquery autocomplete
Call the google autocompleteService.getQueryPredictions method
Filter out the results you want and return them as the "response" callback of the jquery autocomplete.
Optionally, if you need more detail about the selected item from Google, override the select function of the jquery autocomplete and make a call to Google's PlacesService.getDetails method.
The below assumes you have the Google api reference with the "places" library.
<script src="https://maps.googleapis.com/maps/api/js?key=[yourKeyHere]&libraries=places&v=weekly" defer></script>
var _autoCompleteService; // defined globally in script
var _placesService; // defined globally in script
//...
// setup autocomplete wrapper for google places
// starting point in our city
var defaultBounds = new google.maps.LatLngBounds(
new google.maps.LatLng('42.9655426','-85.6769166'),
new google.maps.LatLng('42.9655426','-85.6769166'));
if (_autoCompleteService == null) {
_autoCompleteService = new google.maps.places.AutocompleteService();
}
$("#CustomerAddress_Street").autocomplete({
minLength: 2,
source: function (request, response) {
if (request.term != '') {
var googleRequest = {
input: request.term,
bounds: defaultBounds,
types: ["geocode"],
componentRestrictions: { 'country': ['us'] },
fields: ['geometry', 'formatted_address']
}
_autoCompleteService.getQueryPredictions(googleRequest, function (predictions) {
var michiganOnly = new Array(); // array to hold only addresses in Michigan
for (var i = 0; i < predictions.length; i++) {
if (predictions[i].terms.length > 0) {
// find the State term. Could probably assume it's predictions[4], but not sure if it is guaranteed.
for (var j = 0; j < predictions[i].terms.length; j++) {
if (predictions[i].terms[j].value.length == 2) {
if (predictions[i].terms[j].value.toUpperCase() == 'MI') {
michiganOnly.push(predictions[i]);
}
}
}
}
}
response(michiganOnly);
});
}
},
select: function (event, ui) {
if (ui != null) {
var item = ui.item;
var request = {
placeId: ui.item.place_id
}
if (_placesService == null) {
$("body").append("<div id='GoogleAttribution'></div>"); // PlacesService() requires a field to put it's attribution image in. For now, just put on on the body
_placesService = new google.maps.places.PlacesService(document.getElementById('GoogleAttribution'));
}
_placesService.getDetails(request, function (result, status) {
if (result != null) {
const place = result;
if (!place.geometry) {
// User entered the name of a Place that was not suggested and
// pressed the Enter key, or the Place Details request failed.
//window.alert("No details available for input: '" + place.name + "'");
return;
}
else {
var latitude = place.geometry.location.lat();
var longitude = place.geometry.location.lng();
// do something with Lat/Lng
}
}
});
}
}
}).autocomplete("instance")._renderItem = function (ul, item) {
// item is the prediction object returned from our call to getQueryPredictions
// return the prediction object's "description" property or do something else
return $("<li>")
.append("<div>" + item.description + "</div>")
.appendTo(ul);
};
$("#CustomerAddress_Street").autocomplete("instance")._renderMenu = function (ul, items) {
// Google's terms require attribution, so when building the menu, append an item pointing to their image
var that = this;
$.each(items, function (index, item) {
that._renderItemData(ul, item);
});
$(ul).append("<li class='ui-menu-item'><div style='display:flex;justify-content:flex-end;'><img src='https://maps.gstatic.com/mapfiles/api-3/images/powered-by-google-on-white3.png' /></div></li>")
}

Consecutive calls to chrome.extension.sendMessage()

I am writing an extension, where I want my own cookies store mycookies in background.js which stores domain, path, key and flags. In my background.js, when an HTTP response is received, I call chrome.extension.sendMessage() for each Set-Cookie header of the the response. So each response call this function many times within few m-seconds. After the response, only the last one or two cookies are stored, the rest are lost.
I did the following experiment. I connected a button with the function update_mycookies. A click on button calls the function immediately and displays the content of mycookies after 2500ms. If I read the store after 2500ms, all four cookies are there if I introduce the delays (as below) in update_mycookies but if I remove the delays, only the last one or two cookies are updated, the rest are lost. The chrome.extension.sendMessage is using `chrome.storage.local.set(). I want my store must be updated with all the cookies in response.
function update_mycookies(){
chrome.extension.sendMessage({"cdomain":".google.com", "path":"/", "key":"PREF"});
setTimeout(function() {
chrome.extension.sendMessage({"cdomain":".google.com", "path":"/", "key":"NID"});}, 1000);
setTimeout(function(){
chrome.extension.sendMessage({"cdomain":".google.it", "path":"/", "key":"PREF"}); }, 1500);
setTimeout(function(){
chrome.extension.sendMessage({"cdomain":".google.it", "path":"/", "key":"NID"}); }, 2000);
}
The onMessage listenr is the following:
chrome.extension.onMessage.addListener(
function(request,sender,sendResponse) {
if (request["cdomain"] && request["path"] && request["key"]) {
chrome.storage.local.get("mycookies", function (items) {
if (items["mycookies"]) {
var found = false;
var entries = items["mycookies"].split(";");
entries.forEach (function (entry) {
var dk = entry.split(",");
if (dk[0] == request["cdomain"] && dk[1] == request["path"] && dk[2] == request["key"])
found = true;
});
if (!found)
chrome.storage.local.set({"mycookies": items["mycookies"] + ";" + request["cdomain"] + "," + request["path"] + ","
+ request["key"] });
}
else
chrome.storage.local.set({"mycookies": request["cdomain"] + "," + request["path"] + "," + request["key"] });
});
}
}

Resources