HOWTO: Add Forms 2.0 JS behaviors inside a Rich Text Area

Custom Forms 2.0 JS behaviors are best managed via an external JS file and <script src="/yourfile.js"> tag in the LP. This allows your code to be updated without touching any other part of the page or form, sharing behaviors across forms & pages, and so on.

At tonight's NYC MUG, my man Nick N. asked if you could put the custom behaviors JS into the form itself via Form Editor.

Indeed, if you want a quick-and-dirty JS enhancement, and you don't want to figure out where in the LP to put your <script> tag[1] or talk to your webmaster, yes, it's possible to use the Forms JS API from a Rich Text area. If you really insist.

That should be good news! The only, let's say, more guarded news is that you have to do it right or can get craaaaazy results.

There's one major concern and one minor concern:

  1. Major: You must ensure the code in your embedded <script> only runs once. Because of the curious way in which forms are rendered, this is harder than you probably think.
  2. Minor: You have to completely hide the Rich Text area so it doesn't show up in the layout, which means hiding its entire form row (margins, padding, et al.).

(2) is easy to accomplish with some CSS. So let's wait on that.

Run only once

Let's see what happens if we naïvely add a RT area containing a <script> with a simple whenReady listener function inside. Note I've put some text at the top of the RT so it looks in-use in Form Editor (“[Form Behaviors JS - Do Not Delete]”). Such text is optional but recommended; otherwise, the RT might be accidentally deleted as it looks empty until you double-click it.

When you load a page with just that one Marketo form in it, you might see the following Console output:

That's the same function run 4 different times even though we only have one Rich Text. Really bad if you're adding event listeners!

This happens because of the way the <form> DOM is built out. As the <script> is ejected and injected into the page repeatedly, it ends up executing its code repeatedly.

And that's not the same function running, technically speaking, but 4 functions that happen to have the same code running in a row. Because they're all separate from each other, they don't share a parent scope in which you could add behaviorsLoaded = true or something like that.

Instead, you can set an HTML data- attribute on the <form> element, since that will of course persist across executions.

Each time the code runs, check for the attribute and return immediately if it's already true:

[Form Behaviors JS - Do Not Delete!]
<script type="application/javascript">
MktoForms2.whenReady(function(form){
  var formEl = form.getFormElem()[0];

  if( formEl.getAttribute("data-inline-behaviors-loaded") == "true" ) {
    return;
  }

  formEl.setAttribute("data-inline-behaviors-loaded", "true");

  // now continue
  console.log("Doing something special");
});
</script>

Now you can see the meat of the code only runs once:

Back to CSS

If you make the Rich Text area the first row in Form Editor, it's easy to select and hide:

Copypasta:

.mktoFormRow:nth-of-type(1) {
    position: absolute;
    visibility: hidden;
}

I'd typically recommend a more resilient method of selecting the right row. But that would likely involve loading my FormsPlus::Tag Wrappers helper JS first… problematic if the whole idea is to consolidate the JS all within the form!


Notes

[1] As a reminder, if not using the Rich Text method described here, put the behaviors <script> just inside the closing </body> tag on a Marketo LP, or anywhere after the form embed code on a non-Marketo LP.