Hey All,
We have many scenarios where it makes sense to use URL Parameters and Referrer Parameters to fill hidden fields, but we are not using the Cookie Values option for anything.
What are the standard use-cases and best practices for using Cookie Values to auto-fill Marketo form fields? I am just looking to understand this at a high level to make sure we can make the most of this functionality.
Thanks!
Solved! Go to Solution.
I'm not Sanford, but maybe you'll find this JS useful. Just don't forget to update domain name.
This script saves UTM parameters in cookies whenever there are any UTM parameters in the URL. It also saves the initial referrer information in a cookie which is ever (365 days) overwritten. The values in the cookies can be read and added to forms or stored in the backend database, etc.
var utmCookie = {
cookieNamePrefix: "",
utmParams: [
"utm_source",
"utm_medium",
"utm_campaign",
"utm_term",
"utm_content"
],
cookieExpiryDays: 365,
// From http://www.quirksmode.org/js/cookies.html
createCookie: function (name, value, days) {
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
var expires = "; expires=" + date.toGMTString();
} else
var expires = "";
document.cookie = this.cookieNamePrefix + name + "=" + value + expires + "; domain=.abilitynetwork.com; path=/";
},
readCookie: function (name) {
var nameEQ = this.cookieNamePrefix + name + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ')
c = c.substring(1, c.length);
if (c.indexOf(nameEQ) == 0)
return c.substring(nameEQ.length, c.length);
}
return null;
},
eraseCookie: function (name) {
this.createCookie(name, "", -1);
},
getParameterByName: function (name) {
name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
console.log(name);
var regexS = "[\\?&]" + name + "=([^&#]*)";
var regex = new RegExp(regexS);
var results = regex.exec(window.location.search);
if (results == null) {
return "";
} else {
return decodeURIComponent(results[1].replace(/\+/g, " "));
}
},
utmPresentInUrl: function () {
var present = false;
for (var i = 0; i < this.utmParams.length; i++) {
var param = this.utmParams[i];
var value = this.getParameterByName(param);
console.log(param + ' = ' + value);
if (value != "" && value != undefined) {
present = true;
}
}
return present;
},
writeUtmCookieFromParams: function () {
for (var i = 0; i < this.utmParams.length; i++) {
var param = this.utmParams[i];
var value = this.getParameterByName(param);
this.writeCookieOnce(param, value)
}
},
writeCookieOnce: function (name, value) {
var existingValue = this.readCookie(name);
if (!existingValue) {
this.createCookie(name, value, this.cookieExpiryDays);
}
},
writeReferrerOnce: function () {
value = document.referrer;
if (value === "" || value === undefined) {
this.writeCookieOnce("referrer", "direct");
} else {
this.writeCookieOnce("referrer", value);
}
},
referrer: function () {
return this.readCookie("referrer");
}
};
utmCookie.writeReferrerOnce();
if (utmCookie.utmPresentInUrl()) {
utmCookie.writeUtmCookieFromParams();
}
Hey Erik,
Yes, I created "original UTM" fields and included both "original" and "last" utm as hidden fields on forms. You will autofill the "Last" utm fields with URL Parameters and autofill the Original utm fields with your stored Cookie values.
Did you also succeeded to do cross domain tracking? Our mainsite is on .com meanwhile our marketo lp are on .eu
Re: Autofill with Cookie Value
reply from Trevor Parsell<https://nation.marketo.com/people/4ed5c3be285642baea928d908bbd728046252be1?et=watches.email.thread> in Products - View the full discussion<https://nation.marketo.com/message/187820-re-autofill-with-cookie-value?et=watches.email.thread#comm...>
Did you also succeeded to do cross domain tracking? Our mainsite is on .com meanwhile our marketo lp are on .eu
This is a very, very different objective and requires a specialized JavaScript solution.
Also, since you're on .eu, are you setting your Munchkin domainLevel correctly?
Hi
Yes the munchkin tracking is working as it should so no problem with that. I’m rather new with the company and the implementation of marketo was done before I started. But is it possible? Is it an own js for the domain tracking or should it be implemented in the existing script?
<https://nation.marketo.com/?et=watches.email.thread>
The Marketo Marketing Nation Community <https://nation.marketo.com/?et=watches.email.thread>
Re: Autofill with Cookie Value
reply from Sanford Whiteman<https://nation.marketo.com/people/63ea064b55e61d1184c4e1e1cc5ee59b25d3a3a1?et=watches.email.thread> in Products - View the full discussion<https://nation.marketo.com/message/187884-re-autofill-with-cookie-value?et=watches.email.thread#comm...>
Yes the munchkin tracking is working as it should so no problem with that.
It's not so much whether it's working on a single domain (hostname) as whether it's working across subdomains of your private domain, see: https://blog.teknkl.com/munchkin-2-letter-tlds-broken/. For example, though you may only be using partner.zyxel.eu, I can see that your sessions wouldn't be shared with www.zyxel.eu should you start using that domain (and of course aren't shared with www.zyxel.com, but sharing across subdomains of zyxel.eu is as simple as changing your Munchkin config).
Is it an own js for the domain tracking or should it be implemented in the existing script?
Not sure if I understand the question but there is no built-in support in the Munchkin library for tracking across different private domains. This is all done using custom development.
Is there a way to start a provate conversation around this?
Sure, we follow each other so you can DM.
Hi, Im trying to implement this in our system. I get the cookie to grab the UTM values and keep it when going to other sites. The problem is to get the cookie to write the info onto the hidden fields. Do I need to set two hidden fields one "orginal utm" and one"last utm"? Would be lovely if someone could do a more step to step guide, or if anyone have time to guide me over chat/pm - thanks
You use Cookie Value when you're purposely persisting values in cookies to pass them around your site -- by far the most common use is to save initial utm_* query params and then restore them into forms later.
Thanks, Sanford. That makes sense and is exactly what we would like to do!
I am new to using cookies. How do you go about setting this up to capture "Original utm" values that can later be accessed when a form is submitted? Our plan is to capture "First utm" values for all form submits.
If you want people's most recent touch on a single origin only (not across origins like pages.example.com and www.example.com, but only on www.example.com) then you don't even need to use cookies. In this blog post I provided an ultra-slim alternative.
Otherwise, I'd use an attribution library that can maintain multiple historical sources, translate referrers into friendly names, stuff like that.
Hi Sandford,
I liked your blog post solution which fit my companies needs very well. I have my test form set up to only trigger when a utm parameter is present, so the trigger is 'fills out form' and the 'query string' contains testUTM. When I navigate to the form page using a real UTM link the campaign trigger works, and your code does not alter the URL because a UTM is present. When I navigate to the form page without a UTM your code works, and attaches the stored UTM part of the URL, however the campaign trigger does not fire. The trigger is set to fire every time so there should be no complications there.
I thought I must have put your code in the wrong JS sequence, but when I checked the dev tools the MKTO form had taken the values your code appended onto the base URL and put them in the hidden fields correctly, yet the campaign trigger still did not fire. How is this possible? What did I do wrong to achieve this result?
Yes both form details are exactly the same except for mktoReferrer. The form submission that works is:
_mktoReferrer: https://www.gtt.com/test-form-page/?utm_source=bing_ads&utm_medium=cpc&utm_campaign=testUTMcampaign
The form submission that doesn't work is :
_mktoReferrer: https://www.gtt.com/test-form-page/
In the form I have the auto fill for the UTM parameters to pull from URL Parameters and they do so just fine. Is the querystring filter in the campaign trigger based off the referrer URL and not the URL of the page the form is on?
The Query String constraint is based on the URL the form is on.
The _mktoReferrer is the URL the form is on. (Don't be confused by the word "referrer", as the referrer of a form post is the page hosting the form, not the page before that.)
It appears that when the form loads it reads the page as a test page with no UTM information tagged onto the URL, but when certain fields go through autofill they do grab the parameters from the URL with the tagged on UTM information supplied by local storage.
I am trying to have a form fill trigger fire only when the querystring constraint contains a certain utm campaign. So is this simply a timing issue?
Will using filled out form with a querystring constraint simply not work in this situation because the URL is changed after an event that reads the URL for any querystrings? Here is the code I used:
<script src="//pages.gtt.com/js/forms2/js/forms2.min.js"></script>
<script>
(function(opts){
var current = {
state : document.location.href,
query : document.location.search.substring(1),
time : new Date().getTime()
},
storage = {
area : window.localStorage,
key : 'last_utm_query'
},
restored,
updated = {};
if (/(^|&)utm_/.test(current.query)) {
storage.area[storage.key] = JSON.stringify({ time:current.time, query:current.query });
} else if (restored = JSON.parse(storage.area[storage.key] || '""')) {
if ( window.MktoForms2 && ( current.time - restored.time <= opts.expireDays * 864E5 ) ) {
updated.state = document.createElement('a');
updated.state.href = current.state;
updated.state.search += (updated.state.search ? '&' : '') + restored.query + '&restored=' + restored.time;
history.replaceState('', {}, updated.state);
}
}
})({
expireDays : 30
});
</script>
<form id="mktoForm_1261"></form>
<script>
MktoForms2.loadForm("XXXXXX", "XXXXXXX, 1261);
</script>
Thanks for any help.
Hey @SanfordWhiteman ,
So at this point I have looked through quite a few posts and I get the feeling that Marketo Forms does a querystring check on the referrer URL before your code appends stored UTM data. I am using this post as reference: https://nation.marketo.com/t5/product-discussions/manually-pass-query-string-vs-hidden-fields/td-p/2... .
I would prefer to use the querystring constraint in the trigger but my work around is below. I am concatenating UTM Campaign with a Timestamp which allows me to trigger on the form fill and filter on the UTM Campaign data change. If there is no UTM present my UTMCampaignTimestamp field should be blank. My question is if I wanted to shorten or lengthen the time the UTM data was stored for a user would a cookie or your local storage technique be better? Do you see any issue with this setup?
<script src="//pages.gtt.com/js/forms2/js/forms2.min.js"></script>
<script>
(function(opts){
var current = {
state : document.location.href,
query : document.location.search.substring(1),
time : new Date().getTime()
},
storage = {
area : window.localStorage,
key : 'last_utm_query'
},
restored,
updated = {};
if (/(^|&)utm_/.test(current.query)) {
storage.area[storage.key] = JSON.stringify({ time:current.time, query:current.query });
} else if (restored = JSON.parse(storage.area[storage.key] || '""')) {
if ( window.MktoForms2 && ( current.time - restored.time <= opts.expireDays * 864E5 ) ) {
updated.state = document.createElement('a');
updated.state.href = current.state;
updated.state.search += (updated.state.search ? '&' : '') + restored.query + '&restored=' + restored.time;
history.replaceState('', {}, updated.state);
}
}
})({
expireDays : 30
});
</script>
<form id="mktoForm_1283"></form>
<script>MktoForms2.loadForm("//pages.gtt.com", "xxxxxxx", 1283);</script>
<script>
MktoForms2.whenReady(function(form){
form.onSubmit(function(form){
var utmcampaigncheck = form.getValues().mktoUTMCampaign;
var timestamp = new Date().toISOString();
var utmcampaignTimestamp = utmcampaigncheck+timestamp;
if (utmcampaigncheck.value=null) {
return
}
else
{
form.addHiddenFields({mktoUTMCampaignTimestamp:utmcampaignTimestamp})
}
});
});
</script>
So at this point I have looked through quite a few posts and I get the feeling that Marketo Forms does a querystring check on the referrer URL before your code appends stored UTM data.
To be clear, you're speaking of the current document URL, not the referrer URL.
The current URL only is the referrer URL of sub-resources like the form post. The referrer of the main resource (the outer document) remains the previous full document.
In any case, yes, the forms library now caches the the current document URL immediately. It didn't do this when my original blog post was published! But you certain don't need any elaborate workarounds for the library to see the modified URL.
Just change this line:
if ( window.MktoForms2 && ( current.time - restored.time <= opts.expireDays * 864E5 ) ) {
to this:
if ( current.time - restored.time <= opts.expireDays * 864E5 ) {
and make sure you run the custom code before you load forms2.min.js.
I updated the blog post:
Yeap that worked and now I don't need any additional filters, just the trigger constraint. Thank you so much, this is a great solution.
I'm not Sanford, but maybe you'll find this JS useful. Just don't forget to update domain name.
This script saves UTM parameters in cookies whenever there are any UTM parameters in the URL. It also saves the initial referrer information in a cookie which is ever (365 days) overwritten. The values in the cookies can be read and added to forms or stored in the backend database, etc.
var utmCookie = {
cookieNamePrefix: "",
utmParams: [
"utm_source",
"utm_medium",
"utm_campaign",
"utm_term",
"utm_content"
],
cookieExpiryDays: 365,
// From http://www.quirksmode.org/js/cookies.html
createCookie: function (name, value, days) {
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
var expires = "; expires=" + date.toGMTString();
} else
var expires = "";
document.cookie = this.cookieNamePrefix + name + "=" + value + expires + "; domain=.abilitynetwork.com; path=/";
},
readCookie: function (name) {
var nameEQ = this.cookieNamePrefix + name + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ')
c = c.substring(1, c.length);
if (c.indexOf(nameEQ) == 0)
return c.substring(nameEQ.length, c.length);
}
return null;
},
eraseCookie: function (name) {
this.createCookie(name, "", -1);
},
getParameterByName: function (name) {
name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
console.log(name);
var regexS = "[\\?&]" + name + "=([^&#]*)";
var regex = new RegExp(regexS);
var results = regex.exec(window.location.search);
if (results == null) {
return "";
} else {
return decodeURIComponent(results[1].replace(/\+/g, " "));
}
},
utmPresentInUrl: function () {
var present = false;
for (var i = 0; i < this.utmParams.length; i++) {
var param = this.utmParams[i];
var value = this.getParameterByName(param);
console.log(param + ' = ' + value);
if (value != "" && value != undefined) {
present = true;
}
}
return present;
},
writeUtmCookieFromParams: function () {
for (var i = 0; i < this.utmParams.length; i++) {
var param = this.utmParams[i];
var value = this.getParameterByName(param);
this.writeCookieOnce(param, value)
}
},
writeCookieOnce: function (name, value) {
var existingValue = this.readCookie(name);
if (!existingValue) {
this.createCookie(name, value, this.cookieExpiryDays);
}
},
writeReferrerOnce: function () {
value = document.referrer;
if (value === "" || value === undefined) {
this.writeCookieOnce("referrer", "direct");
} else {
this.writeCookieOnce("referrer", value);
}
},
referrer: function () {
return this.readCookie("referrer");
}
};
utmCookie.writeReferrerOnce();
if (utmCookie.utmPresentInUrl()) {
utmCookie.writeUtmCookieFromParams();
}