I use this code in GTM , that retrieves a number from another domain and stores it on a dataLayer value, with a dL.push. I want the variable to be available on a specific event.
(function ()
{
var callbackMethodName = "adnscallback";
window[callbackMethodName] = function(data) {
(function() {
dataLayer.push({
'event': 'ga_new',
"id2": data.uid
});
})();
}
var sc = document.createElement("script");
sc.id = "script_" + callbackMethodName;
sc.src = "https://example.site.com/getnumber?callback=" + callbackMethodName;
document.body.appendChild(sc);
})();
But I already create an event called ga_new (hardcoded, that creates all sort of values on the page) and when this codes run, it creates another event called ga_new, instead of pushing the values on the existing event. This creates issues with the tags as they are double firing.
What iam doing wrong and the push, instead of appending the data, it creates the event again?
You can not "update" a custom event. The "event" keyword is a signal to GTM that it needs to update its internal data structure with new values.
The datalayer variable in the page is an array of objects, the internal datastructure is a single object into which new values are merged, and existing values for the same keys are overwritten. GTM overwrites the native implementation of the JS "push" method with its own code that watches for the "event" keyword. So every time it encounters the "event" key changes are triggered.
You either need a different name for the event, or you change the trigger so that it only fires when both the "ga_new" event are set and the data.uid field is set.
Related
I have the below javascript code where I am pulling the destination URL and Alt attribute from the HTML element and trying to push the values in the data-layer.
I have tested the code using the console and it's pulling the desired value. my main concern is how do we push the values in data-layer. Do I need to make modifications to the code to be able to run in customHTML via GTM?
var divHead= document.getElementsByClassName('card__media-overlapping__media');
var eventhandlerdoc = function(event){
var imgURL=event.currentTarget.firstElementChild;
var imgALT=event.currentTarget.firstElementChild.firstElementChild.getAttribute('alt');
dataLayer = [];
dataLayer.push({
'expImgurl': imgURL,
'expImgalt': imgALT
}
for(var index=0; index < divHead.length; index++){
divHead[index].addEventListener('click',eventhandlerdoc,true);}
Your code as-is will break GTM if it is executed after the GTM snippet (since GTM amends the dataLayer's push method with custom code and adds several events). So you would need to make sure this is executed before the snippet, or check if the dataLayer is initalized and reuse it:
window.dataLayer = window.dataLayer || [];
Also the variables will only be available after the next event is pushed to the dataLayer, so you either need to push a custom event or wait until the next (GTM) event before you use them.
And since GTM has it's own click handler I would suggest you use that and move the javascript code to retrieve the attributes to a custom javascript variables.
We initially push an object containing variables to the dataLayer:
dataLayer.push({
'environment': {
'userName': 'abc',
'id': 123,
'clicks': 0
}
});
We now want to increase the value of environment.clicks with every click a user makes.
How to do that? When we push it via
dataLayer.push({
'environment': {
'clicks': 123
}
});
The dataLayer Array may get 10.000s of entries. How to properly update the variable?
The way to update a datalayer variable is to push a variable, either when a "native" GTM event occurs or alongside a custom event. So basically you are it right.
As for your worries that the DL might get to many entries - the dataLayer gets a new entry on every click in any case (GTM adds that itself), so the additional entries for your variable will probably do not matter that much.
If you still want to avoid this you can update a global Javascript variable and use that in GTM. Google Tag Manager has access to all variables on your page (you will still get all the click events in your dataLayer).
The dataLayer also has a set method that allows you to write to the Datalayer directly, which is apparently what you are looking for. You need to acquire your GTM instance and then you can set values:
var gtm = window.google_tag_manager[{{Container ID}}];
gtm.dataLayer.set('balloonsPopped', undefined);
Details are e.g. here in a Bounteous article. You could use this in a custom HTML tag to update the click count before the click event fires your tag.
Also the dataLayer is reset on page load. It would take a hell of a single page app to collect 10 000s of clicks per pageview.
This is tagged Google Analytics. If you plan to track the clicks in GA remember that a GA session expires after 500 clicks, so the results might not be what you expect (also the free version only has only 10M hit per month, click tracking will quickly exhaust this). And of you want to track the number of click in GA then you would need an event or something to track the click, so the number of events is basically the metric you are looking for, or you could create a custom metric and set it to "1" in your GA call (meaning that it will be incremented by one on every call).
Quoting from the documentation:
It's important to note that pushing a variable of the same name as an existing variable to the data layer will cause the existing value to be overwritten by the new value
Simply pushing an entry with the same variable name and the updated value should work.
Couldn't you use a holder? Something like:
var click_value_holder = dataLayer.environment.clicks;
dataLayer.push({
'environment': {
'clicks': (click_value_holder + 1)
}
});
In MultiInput control, when new tokens are added to the control, the old tokens are flushed off.
How do I get the previous tokens, when a new token(s) is added, as when the attachTokenChange event is triggered.
Here is my effort till now:
var oFilter = oSmartFilterBar.getControlByKey("Filter");
// Trying to get the current tokens when the control has focus
oFilter.attachBrowserEvent("onfocus", function(oEvent)
{
oFilter._tempTokens = oFilter.getTokens();
});
oFilter.attachTokenChange(function(oEvent)
{
var existingTokens = oFilter.getTokens();
var oAddedTokens = oEvent.getParameters("addedTokens").token;
}
});
The focus event is not triggered and the attachTokenChange event is triggered every time there is a token change and only the latest values are fetched. Hence the variables existingTokens and oAddedTokens have the same value.
My question is how do I get all the tokens in the control, before the change event is triggered.
You should be able to reconstruct the previous content of the tokens aggregation: old = new - added + removed.
The tokenChange event arguments tell you which tokens have been added and which have been removed. You have to take the type parameter into account.
Example on plunker:
onTokenChange: function(oEvent){
var newCollection = oEvent.getSource().getTokens();
var parameters = oEvent.getParameters();
if(parameters.type==="tokensChanged"){
//You can ignore the added, removed and removeAll type events
// as tokensChanged is always fired.
var oldCollection = newCollection.filter(function(t){return parameters.addedTokens.indexOf(t)<0});
Array.prototype.push.apply(oldCollection,parameters.removedTokens);
//oldCollection: here you are.
}
}
I have a div which contains message updates sent by users. How can I assign a javascript variable to this div so that it's value is always the current content of the div which is consistently updating with messages?
var x = document.getElementById("MessageBox"); // only equals value at the
// time rather than present value
The easiest way to accomplish this would be to assign an .onchange event handler to the element, then assign the node's value to a variable held outside the scope of the function:
var myMSGvalue;
document.getElementById("MessageBox").onchange = function () {
myMSGvalue = this.value;
// this will work for <input>, not <div>
}
If you really need to assign your data to an html element, you should use an <input type="hidden">.
However, it would be much better to assign the data to a variable before you append it to the page. For example, let's say you have a web socket:
var messages = [];
socket.on("msg", function (msg) {
messages.push(msg);
// msg: { content: "Hello!", author: "notSure" };
functionToAppendToPage(msg);
})
Now, you can see all the messages received in messages and messages[messages.length - 1] will return the last message received (provided at least one message has been received).
If your message box field getting data using any Api or through Ajax. You can update the variable before assigning value to message box.
I am building out a library that is roughly laid out as follows. There are a couple of variables, an event handler and a method which results in the related event firing. Finally I extend the deal with a property. It goes like this (I've added some ///... where I've clipped out other code):
$.collidR = function (options) {
/// ...
var hubName = 'CollidRHub';
var hubProxy = connection.createHubProxy(hubName);
/// ...
hubProxy.on('registrationComplete', function (username, hasChanges) {
$(window).triggerHandler(events.onRegistrationComplete, { username: username, hasChanges: hasChanges });
log(username + " has successfully registered for this entity.");
// capture current user
this._currentUser = username;
// hook for catching up when user joins after edits
if (hasChanges) {
log("There are outstanding changes for this entity...");
}
});
/// ...
this.registerClient = function () {
/// does some stuff that makes registrationComplete fire
/// ...
};
};
Object.defineProperty($.collidR.prototype, "currentUser", {
get: function () {
return this._currentUser ? this._currentUser : "";
}
});
Note that above the this._currentUser = username bit does not seem to work. I think it's a problem with encapsulation, which is what this question is building towards.
In a separate but related library I create an instance of collidR, and I need to respond to an event. I've got the handler for that set up as follows:
$(window).on(collidR.events.onEditorsUpdated, function (e, data) {
/// ...
users.forEach(function (user) {
var currentUser = collidR.currentUser;
// here, currentUser is always default of ""
if (user != currentUser) {
/// ...
}
});
});
Here is what I am seeing:
My registrationComplete event fires and the handler is called successfully
From a debugger, this._currentUser is undefined before the value is set
The line this._currentUser = username is executed and the value is set
When the onEditorsUpdated event fires, collidR.currentUser is always the default value (an empty string) in my handler
What feels out-of-order is where I define the property -- after the rest of the object. It's as though I'm altering the prototype of the object after I define a method which tries to reference the property...this can't be right.
I've also tried this.currentUser (in the internal method), but had the same result.
I had assumed that if the prototype was extended before the internal method was invoked that when I did var currentUser = collidR.currentUser; that I would get the value from the property, but it's always an empty string.
Is there a way to register the property earlier?
Is there a correct way to set the value so that I will be able to access it later via the exposed property?
Because this in this._currentUser = username; isn't what you think it is. The value of this in JavaScript depends on how the function was called. I assume that inside the handler, it's now referring to hubProxy or some other object other than collidR.
Assuming that your entire plugin's this refers to collidR (which I highly suspect isn't, explained in the section after this), what you can do is save the context of that scope into another variable outside the handler. That way, you can refer to the outer scope's context via that variable:
// Saving this scope's context
var that = this;
hubProxy.on('registrationComplete', function (username, hasChanges) {
// access _currentUser via the saved context
that._currentUser = username;
});
However, I should warn you about using this. Assuming you are creating a plugin, you'd be calling it like $.collidR({..}). In this case, the this inside the function will refer to $ (which I assume is jQuery) and you are attaching some property to the library. It's risky to attach plugin-specific values to a global library as collisions might occur.
I suggest you store it inside a local variable/object instead.