Reposition Fields in a Marketo Form

Marketo Employee
Marketo Employee

While the Marketo form builder is quite powerful, there are some edge cases where it doesn't produce quite the result that you're looking for.  A common request is to reposition fields after the progressive profiling box, which is a capability that isn't currently available natively.  Fortunately, with some javascript tweaks, we can reposition fields freely.  Let's take a look at the code:

/*

jQuery must be loaded on the page

use moveMktoField to position the field designated by FieldName relative to the field designated by pivotName

*/

function moveMktoField(fieldName, pivotName, beforeOrAfter){

  //find the label element for the field which we want to move

  var labelForFieldToMove = $('[for="' + fieldName + '"]');

  //find the label element for the field we want to position relative to

  var labelForPivot = $('[for="' + pivotName + '"]');

  //get the mktoFormRow parent of each

  var fieldToMove = getParentWithClass(labelForFieldToMove, "mktoFormRow");

  var pivot = getParentWithClass(labelForPivot, "mktoFormRow");

  //insert the field before or after the pivot based on the setting

  if (beforeOrAfter == "before"){

       fieldToMove.insertBefore(pivot);

  } else if (beforeOrAfter == "after"){

       fieldToMove.insertAfter(pivot);

  } else {

       console.log("argument 'beforeOrAfter' must be one of 'before' or 'after'");

       return null;

  }

}

//inserts multiple fields in order where the first in the fields array is adjacent to the pivot, and the last is furthest

function moveMktoFields(fields, pivotName, beforeOrAfter){

  moveMktoField(fields[0], pivotName, beforeOrAfter);

  for ( i = 1; i < array.length; i++ ){

       moveMktoField(fields[i], fields[i - 1], beforeOrAfter);

  }

}

function getParentWithClass(elem, withClass) {

  //Check the parent to see if it has the desired class

  if ( elem.parent().hasClass(withClass) ) {

       return elem.parent();

  } else {

       //if not, call self recursively on the immediate parent

       return (getParentWithClass(elem.parent(), withClass) );

  }

}

With the above, we can use the moveMktoField function to reposition one field relative to another.  You can also use moveMktoFields to move the fields as a group when you pass an array of field names to it.  Click here to see a live demo, moving First Name after Last Name.

2163
43
43 Comments
Marketo Employee

You've kept the setTimeout included, which is causing the 2 second delay.  This is in my demo script just to show it in action so you can see the difference between pre and post-execution.  Just remove the setTimeout and you should be good:

This:

MktoForms2.whenReady(function(form){

  moveMktoField("FirstName", "LastName", "after");

});

Instead of this:

MktoForms2.whenReady(function(form){

setTimeout(function(){

  moveMktoField("FirstName", "LastName", "after");

}, 2000);

});

You also only need to call whenReady once, so both of your calls to moveMktoField can be inside of that single callback.

Anonymous
Not applicable

Thanks again for the fast reply. I removed the setTimeout function, but then the Javascript wasn't executed.

My workaround now is to set the time from 2000 to 1, so it's reordering the fileds nearly instant after loading.

That fits my customers needs in the best way because they wanted a fast solution.

Anonymous
Not applicable

Forgive a newbie question but where do I put that code?  I'm not a developer but am comfortable with copy/paste and rearranging the code. Thanks!

Anonymous
Not applicable

I added the code at the bottom of the Landingpage Template (used the Guided Editor). This works best for me. Hope the Developers will allow reordering of all form fields even under the Progressive Profiling part in the near future. Our Marketing Crowd needs an easy drag and drop solution here fast.

Anonymous
Not applicable

Could you give more detail on what you did?  I'm not sure where to put it in a blank template.  So far, it just shows up as text, which is not what I want.

Marketo Employee

Hi Jennifer,

It is probably rendering as text since you do not have script tags wrapping it.  You would add this inside of an HTML block on your LP like so:

<script>

//code goes here

</script>

You could also do the same in a template.

Anonymous
Not applicable

Hey Kenny Elkington​, thanks for this nice solution. As the general use case of this is to place the checkboxes after progressive fields at the base of a form, how are you assigning the pivotName value, when you don't know which particular progressive field is going to be showing up for a user?

If this isn't possible with the current code I will try and post a solution here.

Marketo Employee

This solution doesn't currently account for it.  You'll need to provide a list of fields which can be profiled and then determine which are present and which you want to pivot on.

Anonymous
Not applicable

Here's a solution that allows you to move any elements to the bottom of the form, beneath any progressive form fields, and just above the button.

(Sorry, I don't know how to post code formatted as code here, like Kenny did in the initial post)

function moveMktoFieldsToBottom(fieldNames) {

  for ( i = 0; i < fieldNames.length; i++ ){

    var elementToMove = $('[for="' + fieldNames[i] + '"]').parents(".mktoFormRow");

    try {

      elementToMove.insertBefore(".mktoButtonRow");

    } catch(err) {

      console.log("Error: " + err.message);

    } 

  }

}

And here's a version of the moveMktoField from above that's a little cleaner:

function moveMktoField(fieldName, pivotName, beforeOrAfter){

  // set the default

  beforeOrAfter = beforeOrAfter || 'after';

  var elementToMove = $('[for="' + fieldName + '"]').parents('.mktoFormRow');

  var pivotElement = $('[for="' + pivotName + '"]').parents('.mktoFormRow');

  //insert the field before or after the pivot based on the setting

  if (beforeOrAfter == "before"){

      elementToMove.insertBefore(pivotElement);

  } else if (beforeOrAfter == "after"){

      elementToMove.insertAfter(pivotElement);

  }

}

Anonymous
Not applicable

This is an excellent JS that addresses exactly what I needed to do.  However, I'm having difficulty getting it to work when the setTimeout function is removed.  Here are 2 samples I made.  The goal is to bring the "Opt-In" checkbox to be positioned after the progressive profiling fields.

This page has a setTimeout set to 1000

http://www2.lucidas.co.jp/rearrange_test_timed.html

...whereas this one has no setTimeout, as shown in your code snippet.
http://www2.lucidas.co.jp/rearrange_test_nowait.html


When I try to run the second one in order to get instant results, it won't work, and my debugger says "RangeError: Maximum call stack size exceeded."
Knowing me, I probably am missing something stupid like a closing bracket or a double quote, but for the life of me it looks legit, although I'm wondering if trying to jump multiple fields rather than one may have some implications?