sample code of marketo preference center

A Marketo Preference Center Was the Coolest Thing Sponge Built in 2019

Feast your eyes on this Marketo preference center Sponge built for MicroStrategy.   Looks harmless, right?

MicroStrategy preference center

Except it translates into 9 other languages using a single form and a single landing page.  Here’s the full story.

If you’re a visual learner,  follow along in this video tour.  Otherwise, read on!

 

Shelby:  As Sponge looks back on 2019, this MicroStrategy Marketo preference center comes up again and again. What was this project and why did MicroStrategy update their preference center?

Jessica:  MicroStrategy has hundreds of campaigns that go out every quarter.  In each of those emails, the footer–like a lot of companies–went to a straight unsubscribe page.

>>Related: How to Re-run Salesforce Lead Assignment Rules: Process Builder & Apex<<

Once MicroStrategy lost those contacts, they lost them for every single campaign and every single segment. But MicroStrategy had a hunch that people were still really interested in attending their events. MicroStrategy also has a pretty robust community, and they were starting to send a lot more emails.

First we built clear segments so MicroStrategy could run campaigns to specific parts of the database.

Then we needed to stem the bleeding from the unsubscribes.  Contacts wanted to hear from MicroStrategy, but they didn’t want so many emails. Previously contacts had an all-or-nothing choice. Sponge worked with the demand gen and marketing ops team to identify:

  • what are the different types of communications they were sending out?
  • what were the big buckets people might want to subscribe and/or unsubscribe from?

MicroStrategy uniquely wanted to introduce language preference and role/persona. MicroStrategy translates all of their emails into nine different languages. They wanted to implement language preference so that they were actually communicating in the language of their contact’s choosing.

Shelby:  How long did this project take?

Jessica:  The actual build-out for the preference center only took a couple of weeks.

Shelby:  That’s awesome. And Todd, as the architect, that’s when you came in.

Todd: Yeah. I build a lot of Marketo preference centers.  They’re not usually this interesting.  The standard approach to a preference center is either:

  • You deploy the form in one language and too bad if that isn’t the one your contacts actually want to use.
  • Or, you have a bunch of different pages with different forms, each one in a different language, which of course creates a whole lot of overhead for marketing ops. It makes reporting a nightmare and introduces a lot more opportunity for user error.

This project is super exciting because we’re supporting so many languages.  I stumbled upon this blog post by Sanford Whiteman where he unpacks the standard way of embedding Marketo forms and splits it into different parts. This allows you to do some really cool stuff on the front end and translate the form dynamically. So, instead of having 10 different forms and 10 different pages, it’s one page with one form that dynamically loads in the user’s preference. It starts with a default based on a token value. But since part of this project is declaring your preferences, we wanted to have it reload in real time if users change their preference on the form.

Behind the scenes, it’s pretty cool how this works. The bulk of the work is driven by a dictionary based on a JavaScript object of all the different languages and the fields and values used in each language. On the Marketo back end, it’s just one form and one landing page. When you open up the landing page, it’s a standard-looking preference center, but the logo and header text are dynamic content based on the token value and the segmentation, and the form is translated dynamically based on the dictionary.

Here’s a sample demo from Sanford of how the translation works–including for picklist fields.

The use case identified on Sanford’s blog post talks about the real-time translation, but we extended this a little bit to do dynamic reloading, as well.

When the user selects a new language, we append a URL parameter to override the dynamic content and reload the page.  It also translates all of the stuff within the form into the new language too.  There’s a little bit of JavaScript here to set the runtime language that’s based on the query string parameter of L9 Languages–this is the segmentation that drives the dynamic content. We’re using a JavaScript library called URI.js to handle the parsing of the page URL to determine if there is a runtime language to be used and to build the URL to redirect the user to if they change their language preference.

An additional layer that’s kind of cool:, maybe a user starts filling this out, but decide they want to change their language again. We actually store what they filled out already. So even though the page reloads, we don’t lose what they’ve already filled out.  For this we’re using the browser’s sessionStorage to save and restore (and then delete from sessionStorage after restoration) completed form values on a page reload.

<script src = "https://[Your Hosted Version of URI.js]/URI.js"></script>
<script>
//define supported languages  	
var supportedLangs = ["English", "Chinese", "Japanese", "Korean", "Spanish", "Portuguese", "French", "German", "Polish", "Italian"];
var pageURI = new URI();
//define variable with name of Marketo Segmentation for dynamic content	
var langParam = "L9 Languages";
//get value of query string parameter for dynamic content
var runtimeLang = pageURI.search(true)[langParam];
var langpref;
if query string value is supported, set language preference to runtimeLang
if (supportedLangs.indexOf(runtimeLang) != -1) {
    langpref = runtimeLang;
} else {
    //otherwise, use lead token value--default to English      
    langpref = "{{lead.Language_Preference__c:default=English}}";
}
//define current session language    
var session = {
    lang: langpref
};
</script>

<!--put this after loading the Marketo Forms 2.0 library-->
<script>
//define which language to pre-populate in the form 
var preFillLangPref;
if (runtimeLang) {
    preFillLangPref = runtimeLang;
} else {
    preFillLangPref = "{{lead.Language Preference}}"
}
MktoForms2.whenReady(function(form) {
    if (sessionStorage.getItem('formData')) {
        //if there is stored form data in sessionStorage (due to a reload of the page), 
        //restore the data from the partially filled-out form and then clear the sessionStorage item 
        var preFillData = JSON.parse(sessionStorage.getItem('formData'));
        form.setValues(preFillData);
        sessionStorage.removeItem('formData');
    } else {
        //otherwise, pre-populate the form as normal based on Marketo data 
        var init_fields = {
                "Email": "{{lead.Email Address}}",
                "Role_Marketo__c": "{{lead.Marketo Role}}",
                "Country": "{{lead.Country}}",
                "Language_Preference__c": preFillLangPref
            },
            init_checkboxes = {
                "Unsubscribed": "{{lead.Unsubscribed}}",
                "Product_and_Support_Updates__c": "{{lead.Product and Support Updates}}",
                "Premium_Content__c": "{{lead.Premium Content}}",
                "Events__c": "{{lead.Events}}",
                "Community_Newsletter__c": "{{lead.Community Newsletter}}",
                "Education_and_Professional_Services__c": "{{lead.Education and Professional Services}}"
            };
        MktoForms2.$.each(init_checkboxes, function(key, val) {
            if (val) {
                init_fields[key] = "yes"
            }
        });
        form.vals(init_fields);
    }
});
MktoForms2.whenReady(function(form) {
    var formEl = form.getFormElem()[0],
        langField = "Language_Preference__c",
        langEl = formEl.querySelector("[name='" + langField + "']");
    langEl.addEventListener("change", function(e) {
        //if language preference is changed in the form, reload the page in the newly selected language 
        if (form.getValues()[langField] && form.getValues()[langField] != session.lang) {
            var redirectURI = new URI();
            //set query string parameter to new language 
            redirectURI.setSearch(langParam, form.getValues()[langField]);
            //save partially completed form data in sessionStorage 
            window.sessionStorage.setItem('formData', JSON.stringify(form.getValues()));
            //reload the page using the new redirectURI with the new language preference 
            document.location = redirectURI.toString();
        }
    });
}); 
</script>

And similarly, if somebody does “unsubscribe from all”, there’s a little script to remove all their other preferences so that it’s very clear to the user.

<script>
// <![CDATA[
MktoForms2.whenReady(function(form) {
    //get form fields
    var formEl = form.getFormElem()[0],
        unsubscribeField = 'Unsubscribed',
        unsubscribeEl = formEl.querySelector('#' + unsubscribeField),
        checkboxEls = formEl.querySelectorAll('INPUT[type="checkbox"]');
    Array.prototype.forEach.call(checkboxEls, function(itm) {
        //if unsubscribed is true, uncheck and disable all other checkbox fields
        if (itm !== unsubscribeEl && unsubscribeEl.checked) {
            itm.checked = false;
            itm.disabled = true;
        } else {
            itm.disabled = false;
        }
    });
    //add event listener to unsubscribed field
    unsubscribeEl.onclick = function(e) {
        //not very DRY, but purposefully re-querying checkbox fields in case some items have been hidden
        //or made visible due to Marketo form visibility rules
        var formEl = form.getFormElem()[0],
            unsubscribeField = 'Unsubscribed',
            unsubscribeEl = formEl.querySelector('#' + unsubscribeField),
            checkboxEls = formEl.querySelectorAll('INPUT[type="checkbox"]');
        Array.prototype.forEach.call(checkboxEls, function(itm) {
            if (itm !== unsubscribeEl && unsubscribeEl.checked) {
                //if unsubscribed is true uncheck and disable all other checkboxes
                itm.checked = false;
                itm.disabled = true;
            } else {
                //if unsubscribed is false, re-enable other checkboxes
                itm.disabled = false;
            }
        });
    }
});
// ]]>
</script>

The checkboxes script was inspired by this post on Marketo Nation (again from Sanford) with a little extra in it to also disable the other checkboxes and account for any checkbox fields that may have been hidden or revealed due to Marketo visibility rules.

What’s super cool?  You could extend this across your entire website and translate any form on the fly in whatever user language you want. You have one script with your language definitions, and presumably the form fields you’re asking for aren’t really changing that often.

Jessica: Yeah, MicroStrategy is excited about this.  They have hundreds of different assets, x9 forms and x9 landing pages. This would cull the number of landing pages and forms down to 1.  They’ll save time on maintenance, reduce user errors, and clean up their Marketo instance.  We actually have a meeting later this week to talk about how to scale this across all their other pages.

Shelby: And it launched pretty recently, right?

Jessica: Yeah, just a few weeks ago, but the early results are great.  We had a few hundred contacts fill out the preference form within the first week. 179 contacts adjusted their settings, but were still subscribed to at least one communication. 106 contacts unsubscribed from everything.

So most people just adjusted their settings instead of unsubscribing. We saved about 63% of contacts who would have ordinarily unsubscribed from everything. They’re still on the list, but now they’re just prioritizing which emails they actually want to receive.

Shelby:  That’s so great.  Who would have guessed a preference center would be this cool.

Todd:  It’s quite possibly the coolest thing I’ve ever built.

 

Ready to unleash the full power of your marketing tech stack, without hiring a full time employee?  

Learn about our unlimited Marketo/Hubspot admin support.

Sponge | Marketing and Sales Ops Consultants

Get a Free System Audit Today!

Whether you inherited a new instance or just want a second opinion, we'll dive in and benchmark your setup.

Sponge | Marketing and Sales Ops Consultants

Download Resource

Use this form to recieve your free resource in your inbox today!