Solved

Multi-Field, Multi-Step Form šŸ”„


Userlevel 7
Badge +1
  • Former Community Manager @ Unbounce
  • 831 replies

Every so often, the Unbounce Community is blessed by some absolute freakinā€™ ROCKSTARS like @Caroline. A few months back, she took it upon herself to share this boss workaround for a multi-step form and it blew our minds. This deserved to be a standalone post within our Tips and Scripts.

Wonā€™t you join me in celebrating the awesomeness that is Caroline?! Itā€™s community members like her that make this such a badass online community! 🙌


33%20PM

Here is what we have been using for multi fields on multi steps.

This as has worked well for us across many pages & clients.
Here is an example of it in action: http://unbouncepages.com/multi-step-test/

  1. Create form in one column the way you normally would, in the order you want the fields to appear.
  2. Add the following script (before body end tag), name it ā€œMulti Step 1ā€
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" type="text/javascript"></script>
<script>
  function UnbounceMultiStep(form, options) {
    // Validate arguments
    if (!form.is('.lp-pom-form form')) {
      return console.error('jQuery.unbounceMultiStep must be called on an Unbounce form.');
    }

    if (typeof options !== 'object') {
      return console.error('No options were passed to jQuery.unbounceMultiStep');
    }

    this.options = options;

    // Store DOM elements
    this.form = form;
    this.formContainer = this.form.closest('.lp-pom-form');
    this.fields = this.form.find('div.lp-pom-form-field');
    this.fieldsByStep = [];
    this.currentStep = 0;
    this.forwardButton = this.form.find('button[type=submit]').eq(0);

    // Verbiage
    this.text = {};
    this.text.next = this.options.nextButton;
    this.text.showSteps = this.options.showSteps;
    this.text.back = this.options.backButton;
    this.text.submit = this.forwardButton.text();

    this.init();
  }

  UnbounceMultiStep.prototype.init = function() {
    this.formContainer.addClass('multistep initial');
    this.form.attr('id', 'fields');

    // Add progress bar
    this.formContainer.prepend('<div id="progress-bar"></div>');
    this.progressBar = jQuery('#progress-bar');

    // Replicate Unbounce's field spacing
    var height = parseInt(this.fields.eq(0).css('height'), 10);
    var top = parseInt(this.fields.eq(1).css('top'), 10);
    this.fields.css('margin-bottom', top - height + 'px');
    this.progressBar.css('margin-bottom', top - height + 'px');

    // Set up fieldset elements for each step
    for (var i = 0; i < this.options.steps.length; i++) {
      console.log('Adding new fieldset.');
      this.form.find('.fields').append('<fieldset></fieldset>');
    }
    this.steps = this.form.find('fieldset');
    this.steps.addClass('step');

    // Sort fields into new steps
    var currentField = 0;
    for (currentStep = 0; currentStep < this.options.steps.length; currentStep++) {
      this.progressBar.append(
        '<div class="step">' +
          '<span class="num">' +
          (currentStep + 1) +
          '</span>' +
          '<span class="title">' +
          this.options.steps[currentStep].title +
          '</span>' +
          '</div>'
      );
      this.fieldsByStep[currentStep] = [];
      for (i = 0; i < this.options.steps[currentStep].fields; i++) {
        console.log('Field ' + currentField + ' -> step ' + currentStep);
        this.fields.eq(currentField).appendTo(this.steps.eq(currentStep));
        this.fieldsByStep[currentStep].push(this.fields.eq(currentField));
        currentField++;
      }
    }

    // Add any remaining fields to last step
    if (currentField < this.fields.length) {
      currentStep--;
      for (i = currentField; i < this.fields.length; i++) {
        console.log('Field ' + currentField + ' -> step ' + currentStep);
        this.fields.eq(currentField).appendTo(this.steps.last());
        this.fieldsByStep[currentStep].push(this.fields.eq(currentField));
        currentField++;
      }
    }

    this.progressBarItems = jQuery('#progress-bar .step');

    // Add a back button
    this.backButton = this.forwardButton.clone().insertBefore(this.forwardButton);
    this.backButton.addClass('back-button');
    this.backButton.children('span').html(this.text.back);

    // Add event listeners
    this.backButton.bind('click.unbounceMultiStep', this.backHandler.bind(this));

    this.forwardButton.bind('click.unbounceMultiStep', this.forwardHandler.bind(this));

    this.fields.find(':input').bind('invalid', function(event) {
      // Prevent browser from trying to focus invalid inputs on non-visible steps
      if (jQuery(event.currentTarget).closest('fieldset.active').length === 0) {
        event.preventDefault();
      }
    });

    // Show first step
    this.goToStep(0);
  };

  UnbounceMultiStep.prototype.goToStep = function(newStep) {
    // Make sure we aren't going to a step that doesn't exist
    if (newStep < 0 || newStep >= this.steps.length) {
      return false;
    }

    this.steps
      .eq(this.currentStep)
      .removeClass('active')
      .hide();

    this.steps
      .eq(newStep)
      .addClass('active')
      .fadeIn();

    this.progressBarItems.eq(this.currentStep).removeClass('active');
    this.progressBarItems.eq(newStep).addClass('active');

    this.formContainer.toggleClass('initial', newStep === 0);

    // Update the label of the forward button
    var current = parseInt(newStep) + 2;
    var total = this.steps.length;
    var nextText = this.text.showSteps
      ? this.text.next + ' (Step ' + current + ' of ' + total + ')'
      : this.text.next;
    var submitText = this.text.submit;

    var forwardButtonLabel = newStep === this.steps.length - 1 ? submitText : nextText;

    this.forwardButton.children('span').html(forwardButtonLabel);

    this.currentStep = newStep;
  };

  UnbounceMultiStep.prototype.isValid = function() {
    return this.steps.eq(this.currentStep)[0].querySelectorAll(':invalid').length === 0;
  };

  UnbounceMultiStep.prototype.forwardHandler = function(event) {
    if (!this.isValid()) {
      // If one or more fields on the current step is invalid, prevent going to next step. Allow the
      // default click action, which will display the validation errors.
      return;
    }

    if (this.currentStep === this.steps.length - 1) {
      // If we are on the last step, go back to the first step, and allow the default click action,
      // which will submit the form.
      this.goToStep(0);
    } else {
      event.preventDefault();
      this.goToStep(this.currentStep + 1);
    }
  };

  UnbounceMultiStep.prototype.backHandler = function(event) {
    event.preventDefault();
    this.goToStep(this.currentStep - 1);
  };

  jQuery.fn.unbounceMultiStep = function(options) {
    this.map(function(index, element) {
      new UnbounceMultiStep(jQuery(element), options);
    });
    return this;
  };
</script>
  1. Add another script (before body end tag), name it ā€œMulti Step 2ā€
<script>
  jQuery('.lp-pom-form form').unbounceMultiStep({
    steps: [
      { title: '', fields: 3 },
      { title: '', fields: 3 },
      { title: '', fields: 6 },
    ],
    nextButton: 'Continue Ā»',
    backButton: 'Back',
  });

  jQuery('form#fields').css('margin-top', '20px');

  jQuery('#progress-bar').hide();

  jQuery('fieldset.step:last-of-type div.lp-pom-form-field').css('float', 'left');

  jQuery('fieldset.step:last-of-type div.lp-pom-form-field').css('width', '150px');

  jQuery('fieldset.step:last-of-type div.lp-pom-form-field input').css('width', '140px');

  jQuery('fieldset.step:last-of-type div.lp-pom-form-field select').css('width', '140px');

  jQuery('fieldset.step:last-of-type div.lp-pom-form-field:last-of-type select').css('width', '140px');

  jQuery('#Country').val('United States');

  var disclaimer =
    '<p style="margin-top: 55px; font-family:arial,helvetica,sans-serif;font-size: 10px; color: #636363; line-height: 12px; padding: 40px 0 20px 0;"> I like cats because they are fat and fluffy always ensure to lay down in such a manner that tail can lightly brush humans nose , for run outside as soon as door open. Chase ball of string lounge in doorway or give me some of your food give me some of your food give me some of your food meh, i dont want it yet plan steps for world domination so touch water with paw then recoil in horror for chase dog then run away i shredded your linens for you.</p>';

  jQuery('fieldset.step:last-of-type').append(disclaimer);

  jQuery(window).keydown(function(event) {
    if (event.keyCode === 13) {
      event.preventDefault();
      return false;
    }
  });
</script>
  1. Add the CSS (add stylesheet) - This is copied straight from the page I built for the demo.
<style>
  html,
  * {
    -webkit-font-smoothing: antialiased !important;
  }

  /* Style Form */

  .multistep #fields {
    height: auto !important;
  }

  .multistep #fields .step {
    height: auto !important;
    position: absolute;
    top: 0;
    display: none;
  }

  .multistep #fields .step.active {
    position: relative;
  }

  .multistep #fields .step .lp-pom-form-field {
    position: relative !important;
    top: auto !important;
  }

  .multistep .lp-pom-button {
    position: relative !important;
    top: 0 !important;
    width: 60% !important;
    display: inline-block !important;
    float: right;
    letter-spacing: 2px !important;
    font-weight: 700 !important;
    text-transform: uppercase !important;
  }

  .multistep .lp-pom-button.back-button {
    margin-right: 0;
    float: left;
    opacity: 0.7 !important;
    background-color: #f1f1f1 !important;
    color: #636363 !important;
    width: 35% !important;
    letter-spacing: 2px !important;
    font-weight: 700 !important;
    text-transform: uppercase !important;
  }

  .multistep.initial .lp-pom-button.back-button {
    display: none !important;
  }

  .lp-pom-form .lp-pom-form-field .text {
    font-family: 'Montserrat' !important;
    font-weight: 700 !important;
    text-transform: uppercase !important;
    color: #3f4144 !important;
    font-size: 12px !important;
    letter-spacing: 2px !important;
  }

  .lp-pom-form .lp-pom-form-field select {
    background-color: #ffffff !important;
    border-radius: 20px;
    font-family: 'Montserrat' !important;
    font-weight: 700 !important;
    text-transform: uppercase !important;
    color: #3f4144 !important;
    font-size: 11px !important;
    letter-spacing: 1px !important;
  }
</style>
  1. Make sure anything in the CSS that has an ID for the form is change to your ID (this one only has two places to replace)

  2. In step #3, in the script called, ā€˜Multi Step 2ā€™ - you can change the number of fields on each step and they will appear in the order you have in them. There you can also rename the ā€˜Backā€™ and ā€˜Nextā€™ buttons and well as add or remove a disclaimer.

  3. SAVE!

This is a bit more on the advanced side, so I will be happy to help anyone with this as much as I can!
Disclaimer - I am not the original creator of this script

Please feel free to add on to this, edit it, remove something, etc.

One issue I have ran into is that the page will still go as long as the form is in the editor UI. If anyone has any ideas of that, please add!

Please make sure to test, test, test!

Enjoy


I think I can speak on behalf of a lot of peopleā€¦

notworthy

icon

Best answer by TimothyDO 7 August 2018, 15:27

View original

100 replies

Userlevel 5
Badge +2

Thank you, @Nicholas! I look forward to seeing any advances on this! #sharingiscaring

Userlevel 7
Badge +4

This is an epic script. So useful! Weā€™re going to give it a try right away and see what we come up with. Thank you for sharing it!

This is truly an amazing script (knowing how difficult is to workaround Unbounceā€™s inflexible structure) Caroline, big thanks for sharing it!

Iā€™m experiencing very odd issue though where Iā€™ve added the email field to the last step (there are 4 steps), set it to required, included email formatting and now when I try to get past the first step, I get a form warning that email field needs to be entered, even though itā€™s hidden in step 4. Spent few hours troubleshooting this issue with Chrome console and defining custom rules and I canā€™t seem to find whatā€™s causing this as phone number validation with formatting enabled works just fine. Very odd. If I make the email field (on the last step) not required, the issue does not persist, however, I need to have it formatted and mandatory.

Iā€™ve seen one person on the other post where youā€™ve shared the script initially had an issue similar to this, youā€™ve answered him in private, maybe you know what the issue was? Thanks again for the script!

Okay think I got the email validation issueā€¦ might also need a little extra for phone validation but the page I am working on doesnā€™t require a phone number so will update if and when I find I need phone validation.

Fix is as followsā€¦ just need a little extra in the validation skipping for different pages 😃

// Only validate fields that are in current step

  $.each(window.module.lp.form.data.validationRules, function() {
    if ( this.email === true ) {
      this.email = $(this).is('.active :input');
      this.required = {
      	depends: function () {
          return $(this).is('.active :input');
      	}
      };
	}
    else if ( this.required === true ) {
      this.required = {
      	depends: function () {
          return $(this).is('.active :input');
      	}
      };
	}
  });

Awesome!

Is there by any chance a way to automatically jump to next question without clicking ā€œnextā€ button?
sthing like this: https://audimagnet.com/comparador/?v=1&adsid=_19630420300699590

Thatā€™d be an extra-awe

Why hasnā€™t unbounce made this a feature that can be easily implemented for those of us who donā€™t know how to code? Iā€™m trying to get this to work properly but Iā€™m completely lost, and the more I look into things as someone who doesnā€™t code, it seems like it would just be easier to use a wordpress template for a landing page and then use one of the many plugins to make a multistep form easily.

Awesome @Caroline a real game changer for conversion freaks like me. Thanks!

Amazing @Caroline ! Thank you so much for sharing. Canā€™t wait to test this out.

Userlevel 5
Badge +2

Hi @Edvinas_Puskorius -

Yes, this is an issue I have been working on for a bit now! Iā€™ve been getting closer, but still havenā€™t completed it. Hopefully someone else in this community has figured it out and can share!

@TimothyDO

Thanks that makes sense and it worked.

One issue Iā€™m trying to figure out is how to stop the form submitting when filling out the form on mobile and tapping ā€œgoā€ on an android keyboard. yet to test on an iphone but iā€™m afraid the same issue will remain - that a form can be submitted without completing all fields by tapping ā€œgoā€ on the mobile keyboard after filling in a field.

EDIT:

The following code will change the behavior of the GO button on mobile devices, so instead of submitting the form pressing GO will just close the keyboard

jQuery(document).ready(function() {
    jQuery('input').keypress(function(e) {
        var code = (e.keyCode ? e.keyCode : e.which);
        if ( (code==13) || (code==10))
            {
            jQuery(this).blur();
            return false;
            }
    });
});

Remember to add script tags at the start and at the end

@Jess

I noticed there is some script that adds a progress bar in lines 39-41

  // Add progress bar
  this.formContainer.prepend('<div id="progress-bar"></div>');
  this.progressBar = lp.jQuery('#progress-bar');

And in the second script there is code that hides it

lp.jQuery('#progress-bar').hide();

Showing it instead of hiding it makes numbers 1,2 and 3 show but no progress bar.

How do I get this progress bar working?

Userlevel 5
Badge +2

With every update to the builder there is almost always an update that needs to be made to the scriptā€¦ which has been happening a lot lately. With this instance, there isnā€™t a great way around this (and it isnā€™t the easiest if you are not familiar with custom scripts or CSS) - but you have to change the buttonā€™s ID attributes. This will remove default styles

First, go into Multi Step 1 and add the following line at the bottom of the Store DOM Elements section:

this.forwardButton.attr('id', 'next-button');

The section will look like this:

  // Store DOM elements
  this.form = form;
  this.formContainer = this.form.closest('.lp-pom-form');
  this.fields = this.form.find('div.lp-pom-form-field');
  this.fieldsByStep = [];
  this.currentStep = 0;
  this.forwardButton = this.formContainer.find('.lp-pom-button').eq(0);
  this.forwardButton.attr('id', 'next-button');

Second, go down to the Add a Back Button section and change it to the following:

  // Add a back button
  this.backButton = this.forwardButton.clone();
  this.backButton.attr('id','back-button');
  this.backButton.insertBefore(this.forwardButton);
  this.backButton.addClass('back-button');
  this.backButton.children('span').html(this.text.back);

Now you will need to add a new stylesheet to edit the buttons. It will look something like the following:
Please note: this is just an example & will need to be completely custom for your design

<style>
#next-button, #back-button {
display: block;
border-radius: 20px;
width: 190px;
position: absolute;
background-color: #522398;
box-shadow: none;
text-shadow: none;
font-family: Roboto;
}
</style>

I hope this helps!

@Caroline

Thank you so much for your fast and very well explained solution!

This did the trick and fortunately I can fix up the CSS 🙂

Has anyone had a chance to post this? It would be super useful.

Badge

Hi all, Iā€™ve been using the multi step form for a while now (love love love it!), however, we recently added a field to collect phone numbers for an SMS reminder. Itā€™s an optional field, but is set to validate the phone number. Problem is, error messages are displaying in a dumb spot.

I tried adding the scripts from [How to] Add Inline Form Error Messages, but it doesnā€™t seem to work. I tried adjusting the location in the CSS field, but I suspect this script is older and Iā€™m guessing the display of errors has changed since then.

Has anyone solved this problem? @Caroline

You can view my test at https://get.mintcro.com/wlp9fb/d.html

If I can solve this, I think I might improve our optin rate 🙂

Thanks in advance!
Kirsti

Really hope so, will continue looking for solution myself.

Hoping someone could help me with this.

Iā€™m trying to figure out how to make the box that I place the form in responsive.

So when you go from step 1, where I only display 1 question.
To step 4, where I display my question + a TOS & Privacy agreement.
Iā€™d like the box that I place this form in to be responsive depending on where the submit button moves too.

How can I do this with code?

Userlevel 5
Badge +2

Hi @raget!

Those are just sections on the page I created - the code will not contain those elements!

Userlevel 5
Badge +2

Hi @gunderw1 !

Go into the CSS for this and edit the top property for the lp-pom-button:

	.multistep .lp-pom-button{
  		position: relative !important;
  		top: 0 !important;
  		width: 60% !important;
  		display: inline-block !important;
  		float: right;
      	letter-spacing: 2px !important;
      	font-weight: 700 !important;
    	text-transform: uppercase !important;
		}

Increase 0 to a unit of your liking (probably around 20 should do it). Please feel free to PM me if you have additional questions or issues!

 if ( this.currentStep === this.steps.length - 1 ) {
    this.form.submit();
    this.goToStep(0);
}

If you look for this section, this is what both submits the form and jumps the page back to step one on submission. You can happily delete the this.goToStep(0); section that will stop the jump back to the first page.
As the what happens on the submission of the form that is controlled from the unbounce editor as this is just calling the standard submission with the this.form.submit();

In this section in the second script you can set the number of pages and number of options to each page. The script above does 3 on the first and second pages, 6 on the last (Any remainder is added to the last page).
Make sense?

Apologies Abraham, I only have this script on one variant at the moment as it goes through my companies QA process.
As Caroline mentions below, updates play havoc with certain scripts and Iā€™ve spent the past week trying to debug issues on our custom inline errors script (which features on all our landing pages :*( ) So the new version of multipage is a little on the back burner for me at the momentā€¦ does however look like I am writing the new version of the multipage compatible inline errors script Iā€™ve been pushing forā€¦

Hi everybody! Thank you for the script!

Is there, by any chance, anyone that could share the script including a progress bar? That would be awesome!

Thanks! 🙂

Try something along the lines of https://stackoverflow.com/questions/3366828/how-to-disable-submit-button-once-it-has-been-clicked

and add that in after the line
if ( this.currentStep === this.steps.length - 1 ) {

That ways it should cut in after the final button press if the form is validā€¦ else your not blocking the persons ability to modify errors and try a submission againā€¦

((would have a better go at this but trying to sort out database issuesā€¦ ))

Hey Fourward,

In the second script, where you set how many fields to show per step you should see a line ā€œnextButtonā€. If you change this that should change the text for your next button, for example, you can change it from ā€˜Continue Ā»ā€™ to ā€˜tell us moreā€™. For your submit button, just change the text of the button connected to the form in the Unbounce editor.

Reply