No matter how tempting, never do this with Marketo string fields:

let jsVar = "{{lead.Some Field}}";

 

Why? Because {{lead.tokens}} are HTML-encoded, not JS-encoded. That is, they’re only supported in text content, like so:

<div>{{lead.Some Field}}</div>

 

Failing to account for this will lead to broken pages — and you won’t find the bugs during testing unless you create a wide enough range of test values.

 

The solution, as I previewed in the recent post on form field visibility, is to output tokens into HTML elements and read them using JS. Simple code for this is below. But first, a little encoding review.

 

What is HTML encoding?

Some characters have special meaning in HTML markup. Most important are the ubiquitous < and > (used to denote HTML tags) and " and ' (used to quote HTML attributes, as in <div class="whatever">).

 

When these characters should be treated as mere text (i.e. as non-special) they must be encoded. Otherwise, you’ll get broken HTML. And even worse, you can be vulnerable to XSS attacks.

 

You’ve surely seen HTML encoding before: &lt; for <, &gt; for >, and &nbsp; for a non-breaking space. In fact, any character can be HTML-encoded using its numeric codepoint, but for readability it’s best to encode only those that can’t be used literally.[1] For example, take this raw unencoded string:

My name is "Joe"

 

Encoding only the mandatory ", it remains readable:

My name is &quot;Joe&quot;

 

Whereas if you encode everything, the HTML source becomes unreadable (though it would display perfectly in the browser itself):

&#x4d;&#x79;&#x20;&#x6e;&#x61;&#x6d;&#x65;&#x20;&#x69;&#x73;&#x20;&#x22;&#x4a;&#x6f;&#x65;&#x22;

 

Marketo uses minimal encoding like PHP’s htmlspecialchars(), so just the 4 characters < > " & are included. This works perfectly for text content. But here’s the rub: code in a <script> isn’t text content. It needs a different type of encoding.

 

What is JS encoding?

Encoding for JS is strikingly different from HTML encoding.

  • some characters happen to be special in both contexts, but they can’t be encoded the same way in JS
  • some characters aren’t special in HTML but are special in JS and must be encoded
  • some characters are special in HTML but aren’t in JS, so they must not be encoded
  • there are even different encoding rules in JS for the same character

For one example, the double quotation mark " is special in both HTML and JS. But you encode it as \" in a JS string, not as &quot;. If you encode the wrong way, your code won’t break at the syntax level, but it will produce poor results. My name is "Joe" will be shown as My name is &quot;Joe&quot; and at the very least look unprofessional.

 

For another, the ampersand & is special in only HTML. If you output it as &amp; in JS, you get those 5 literal characters & a m p ; as they have no special meaning. The end user sees Tom &amp; Jerry instead of Tom & Jerry.

 

For yet another, a line break isn’t special in HTML. But in JS it’s extra special. Inside a double- or single-quoted string, it must be encoded as \n. While inside a backtick-quoted string, you may encode, but it’s optional. And outside of strings, you must not encode it. Confusing, eh?

 

In sum: you can’t output generic HTML-encoded text into a <script>.[2]

 

Solution: output into <datalist><option> elements

The right move is to output {{lead.tokens}} into a hidden element, then read them out using JS. This eliminates any encoding worries because JS sees the decoded value.

 

Technically, any element will do, but I find the perfect fit is a <datalist> with nested <options>:

<datalist class="mktoTokens">
<option label="lead.Channel">{{lead.Channel}}</option>
<option label="lead.Country">{{lead.Country}}</option>
</datalist>

 

A <datalist> is always hidden, and semantically it represents a list of data points. Close enough if you ask me!

 

As you can see, the label is the token name without the {{ }}; the text is the token itself, with the {{ }}).

 

(You can actually use any label, I just find it easier to be consistent.)

 

Reading from a <datalist>: getExportedMktoTokens()

Here’s a simple function getExportedMktoTokens() to fetch one or more tokens:

function getExportedMktoTokens(tokenLabels){
   
   const stor = "datalist.mktoTokens option[label]";     
   const options = Array.from(document.querySelectorAll(stor));

   const ignoreCaseComparator = Intl.Collator(undefined, { sensitivity: "accent" });
   
   let results = [];
   for( const name of [tokenLabels].flat() ){ 
      const value = options.find( option => ignoreCaseComparator.compare(option.label, name) === 0 )?.value;
      results.push(value ?? null);
   }

   return Array.isArray(tokenLabels) ? results : results[0];
}

 

You can call it with one token label or an array of labels:

let leadCountry = getExportedMktoTokens("lead.Country");
let [leadCountry, leadChannel] = getExportedMktoTokens(["lead.Country","lead.Channel"]);

 

As you might’ve picked up, the <option> search is case-insensitive to align with how tokens work. So getExportedMktoTokens("LEAD.COUNTRY") will get the token in <option label="lead.Country">.

 

That’s it! Now you’re ready to safely use Marketo tokens in JS.

 

P.S. A happy side effect is you can also reference tokens from external JS files, which otherwise can’t use tokens.

 
Notes

[1] You actually can use a literal NBSP in HTML if you copy and paste it from somewhere. It’s just impossible to distinguish from a regular space, so you’re likely to delete it accidentally.

 

[2] Theoretically, you can output variables into JS that pass through an HTML encoder if you know with 100.00% certainty they don’t contain any characters that are special in HTML or JS — that is, no HTML encoding will happen and they don’t contain any special JS characters. But you cannot know this with a Marketo string field!