Flip a Marketo form field between “Required” and “Not Required” at runtime

Native Visibility Rules and Progressive Profiling pack immense power. But one thing they can’t do is change whether a field is required: if a field is displayed per VR and/or ProgPro rules, it simply picks up the Required setting from Form Editor.

This means you need JS for advanced behaviors. Say Country should be optional for Home users:

Country and Company are required for Professional and Enterprise. while Number of Employees is displayed (via native VRs) but optional for Professional:

And finally, Enterprise users must provide Country, Company and Number of Employees:

Enter manageFieldRequirement()

Here’s a little JS function that lets you achieve such special effects:

/**
 * Programmatically require/unrequire a Marketo form field
 * @author Sanford Whiteman
 * @version v1.2 2025-08-25
 * @license Hippocratic 3.0: This license must appear with all reproductions of this software.
 *
 */
function manageFieldRequirement(mktoForm, fieldName, userOptions = {}){    
  const defaultOptions = {
    require: false,
    alsoFlipVisibility: false,
    validationMessage: undefined
  };

  const options = Object.assign(defaultOptions, userOptions);

  const formJq = mktoForm.getFormElem();
  const fieldJq = formJq.find(`[name="${CSS.escape(fieldName)}"]`);

  if(!fieldJq.length) {
    return;
  }

  const rowJq = fieldJq.closest(".mktoFormRow");
  const colJq = fieldJq.closest(".mktoFormCol");
 
  const rowColCount = rowJq.find(".mktoFormCol").length; 
  const flippableJq = rowColCount === 1 ? rowJq : colJq;  
  
  const wrapperJq = fieldJq.closest(".mktoFieldWrap");
  const descriptorJq = fieldJq.closest(".mktoFieldDescriptor");

  if(options.require){
    fieldJq.attr("aria-required","true");
    fieldJq.addClass("mktoRequired");
    wrapperJq.addClass("mktoRequiredField");
    descriptorJq.data("mktoFieldDescriptor").required = true;

    if(options.alsoFlipVisibility){
      flippableJq.show();
    }    
  } else {
    fieldJq.removeAttr("aria-required");
    fieldJq.removeClass("mktoRequired");
    wrapperJq.removeClass("mktoRequiredField");
    descriptorJq.data("mktoFieldDescriptor").required = false;

    if(options.alsoFlipVisibility){
      flippableJq.hide();
    }    
  }
  
  if(options.validationMessage){
    descriptorJq.data("mktoFieldDescriptor").validationMessage = options.validationMessage;
  }    
  
}

(With platform features these days, you don’t need jQuery to manipulate the DOM. But here we specifically need jQuery data, so decided to embrace the beast.)

Usage

manageFieldRequirement() takes 3 params:

  • mktoForm - a Marketo Forms 2.0 form object, e.g. the argument passed to a whenReady() listener
  • fieldName - the form field name, a.k.a. SOAP API name
  • userOptions - an object with a few properties:
    • require (Boolean, default false) - require the field or un-require it
    • alsoFlipVisibility (Boolean, default false) - also show the field if required/hide if unrequired
    • validationMessage (String, optional) - custom validation message to swap in

Example: Get the behavior shown in the screenshots above

Inside a Forms 2.0 whenReady() listener, add a DOM change listener. Then do advanced management of Country and Company fields:

MktoForms2.whenReady(function(readyForm){
  const formEl = readyForm.getFormElem()[0];
  
  formEl.addEventListener("change",function(e){
    const currentValues = readyForm.getValues();
    const requestedSubscriptionType = currentValues["SubscriptionType"];
    
    if( !requestedSubscriptionType || requestedSubscriptionType === "Home" ) {
      manageFieldRequirement(readyForm, "Country", { require: false });    
      manageFieldRequirement(readyForm, "Company", { require: false });    
    } else {
      manageFieldRequirement(readyForm, "Country", { require: true, validationMessage: "Please provide Country for Pro and Enterprise accounts." });
      manageFieldRequirement(readyForm, "Company", { require: true, validationMessage: "Company is required for Pro and Enterprise accounts." });
    }

    if( !requestedSubscriptionType || requestedSubscriptionType === "Home" || requestedSubscriptionType === "Professional" ) {
      manageFieldRequirement(readyForm, "NumberOfEmployees", { require: false });    
    } else {
      manageFieldRequirement(readyForm, "NumberOfEmployees", { require: true, validationMessage: "Number of employees is required for Enterprise accounts." });
    }

  });
  
  // initialize state
  formEl.dispatchEvent(new Event("change"));
  
});

N.B. The change listener works even when Visibility Rules are also managing the field because new HTML element(s) are injected synchronously by the VR code, before the event bubbles up to the <form>. This is good!