[How To] Add A Multi-Step Form 2.0


Userlevel 7
  • Former Unbouncer
  • 126 replies

When you’re building your landing pages in Unbounce, you want the conversion process to be as simple as possible. Nothing too distracting, and you certainly don’t want to scare your audience away with long, time-consuming forms.

We’ve built an easy solution for you because, hey, we’re marketers too! :unbounce:

Introducing Multi-Step Forms! 📈 🎉

No more scary, conversion killing forms!
Follow the steps below to implement this handy script into your landing page and try it for yourself! And, as always, remember to A/B test, and keep us posted on your results!

You can see this in action (built in Unbounce) here:


How to Install in Unbounce

Click Here for Instructions

🚨
This is not an official Unbounce feature. This functionality is based entirely on third party code, and has only been tested in limited applications. Since this isn’t a supported Unbounce feature, our support team will be unable to assist if things go awry. So if you are not comfortable with HTML, Javascript and CSS, please consider consulting an experienced developer.


Step 1.

Create your form in Unbounce.

Step 2.

Create a new button element for your ‘Next’ button and one for your ‘Previous’ button. Keep in mind when positioning these buttons (and your form submission button) that only one field will be shown at a time.

Step 3.

Copy this script and paste it into the Javascripts section of your page with placement ‘Before Body End Tag’:

<script type="text/javascript" src="https://rawgit.com/kimmobrunfeldt/progressbar.js/1.0.0/dist/progressbar.js"></script>

<script>
  (function() {
    // Update the following IDs with your own button IDs
    var backButton = document.getElementById('lp-pom-button-14');
    var nextButton = document.getElementById('lp-pom-button-13');
    var showProgressBar = false;

    var submitButton = document.querySelector('.lp-pom-form .lp-pom-button');
    var formContainer = document.querySelector('.lp-element .lp-pom-form');
    var currentField = 0;
    var allFields = document.getElementsByClassName('lp-pom-form-field');
    var errorSpan = document.createElement('span');

    for (i = 0; i < allFields.length; i++) {
      allFields[i].classList.add('hide');
      allFields[i].style.top = '0px';
    }

    backButton.classList.add('hide');
    submitButton.classList.add('hide');

    allFields[0].classList.remove('hide');

    if (allFields[0].querySelector('input')) {
      allFields[0].querySelector('input').focus();
    }

    errorSpan.classList.add('hide');
    errorSpan.style.color = 'red';
    errorSpan.style.position = 'absolute';
    var labelHeight;
    if (allFields[0].querySelector('label')) {
      labelHeight = allFields[0].querySelector('label').clientHeight;
    } else {
      labelHeight = 25;
    }
    errorSpan.style.top = '-' + labelHeight + 'px';
    formContainer.appendChild(errorSpan);

    if (showProgressBar) {
      var progressContainer = document.createElement('div');
      progressContainer.id = 'container';
      formContainer.appendChild(progressContainer);

      var bar = new ProgressBar.Line(container, {
        strokeWidth: 4,
        easing: 'easeInOut',
        duration: 1400,
        color: '#FFEA82',
        trailColor: '#eee',
        trailWidth: 1,
        svgStyle: { width: '100%', height: '100%' },
        from: { color: '#FFEA82' },
        to: { color: '#74D16A' },
        text: {
          style: {
            // Text color.
            // Default: same as stroke color (options.color)
            color: '#fff',
            position: 'absolute',
            right: '0',
            top: '30px',
            padding: 0,
            margin: 0,
            transform: null,
          },
          autoStyleContainer: false,
        },
        step: function(state, bar) {
          bar.setText(currentField + 1 + ' of ' + allFields.length);
          bar.path.setAttribute('stroke', state.color);
        },
      });
    }

    function nextEvent() {
      allFields[currentField].classList.add('hide');

      currentField += 1;
      allFields[currentField].classList.remove('hide');
      if (allFields[currentField].querySelector('input')) {
        allFields[currentField].querySelector('input').focus();
      }

      if (currentField > 0) {
        backButton.classList.remove('hide');
      }

      if (currentField === allFields.length - 1) {
        submitButton.classList.remove('hide');
        nextButton.classList.add('hide');
      }

      updateProgress();
    }

    function backEvent() {
      allFields[currentField].classList.add('hide');
      submitButton.classList.add('hide');

      currentField -= 1;
      allFields[currentField].classList.remove('hide');
      if (allFields[currentField].querySelector('input')) {
        allFields[currentField].querySelector('input').focus();
      }
      if (currentField === 0) {
        backButton.classList.add('hide');
        nextButton.classList.remove('hide');
      }
      if (currentField <= allFields.length - 1) {
        nextButton.classList.remove('hide');
      }

      updateProgress();
    }

    function currentFieldInvalid() {
      var invalidInput = allFields[currentField].querySelector(':invalid');

      if (invalidInput) {
        errorSpan.textContent = invalidInput.validationMessage;
      }

      return Boolean(invalidInput);
    }

    function updateProgress() {
      if (showProgressBar) {
        var barSize = (currentField + 1) / allFields.length;
        bar.animate(barSize); // Number from 0.0 to 1.0
      }
    }

    updateProgress();

    nextButton.addEventListener('click', function(e) {
      if (currentFieldInvalid()) {
        if (allFields[currentField].querySelector('input')) {
          allFields[currentField].querySelector('input').classList.add('hasError');
        } else {
          allFields[currentField].children[1].classList.add('hasError');
        }

        errorSpan.classList.remove('hide');
        e.preventDefault();
      } else {
        if (allFields[currentField].querySelector('input')) {
          allFields[currentField].querySelector('input').classList.remove('hasError');
        } else {
          allFields[currentField].children[1].classList.remove('hasError');
        }

        errorSpan.classList.add('hide');
        nextEvent();
      }
    });

    backButton.addEventListener('click', backEvent);

    submitButton.addEventListener('click', function(e) {
      if (currentFieldInvalid()) {
        if (allFields[currentField].querySelector('input')) {
          allFields[currentField].querySelector('input').classList.add('hasError');
        } else {
          allFields[currentField].children[1].classList.add('hasError');
        }

        errorSpan.classList.remove('hide');
        e.preventDefault();
      } else {
        if (allFields[currentField].querySelector('input')) {
          allFields[currentField].querySelector('input').classList.remove('hasError');
        } else {
          allFields[currentField].children[1].classList.remove('hasError');
        }

        errorSpan.classList.add('hide');
      }
    });

    document.body.addEventListener('keydown', function(e) {
      var keyCode = e.keyCode || e.which;

      // Enter key
      if (keyCode === 13 && currentField < allFields.length - 1) {
        if (currentFieldInvalid()) {
          if (allFields[currentField].querySelector('input')) {
            allFields[currentField].querySelector('input').classList.add('hasError');
          } else {
            allFields[currentField].children[1].classList.add('hasError');
          }

          errorSpan.classList.remove('hide');
          e.preventDefault();
        } else {
          if (allFields[currentField].querySelector('input')) {
            allFields[currentField].querySelector('input').classList.remove('hasError');
          } else {
            allFields[currentField].children[1].classList.remove('hasError');
          }

          errorSpan.classList.add('hide');
          e.preventDefault();
          nextEvent();
        }
      } else if (keyCode === 13 && currentField === allFields.length - 1) {
        if (currentFieldInvalid()) {
          if (allFields[currentField].querySelector('input')) {
            allFields[currentField].querySelector('input').classList.add('hasError');
          } else {
            allFields[currentField].children[1].classList.add('hasError');
          }

          errorSpan.classList.remove('hide');
          e.preventDefault();
        } else {
          if (allFields[currentField].querySelector('input')) {
            allFields[currentField].querySelector('input').classList.remove('hasError');
          } else {
            allFields[currentField].children[1].classList.remove('hasError');
          }

          errorSpan.classList.add('hide');
        }
      }

      if (keyCode === 37) {
        // Left key
        if (currentField > 0) {
          backEvent();
        }
      } else if (keyCode === 39) {
        // Right key
        if (currentField < allFields.length - 1) {
          if (currentFieldInvalid()) {
            if (allFields[currentField].querySelector('input')) {
              allFields[currentField].querySelector('input').classList.add('hasError');
            } else {
              allFields[currentField].children[1].classList.add('hasError');
            }

            errorSpan.classList.remove('hide');
            e.preventDefault();
          } else {
            if (allFields[currentField].querySelector('input')) {
              allFields[currentField].querySelector('input').classList.remove('hasError');
            } else {
              allFields[currentField].children[1].classList.remove('hasError');
            }

            errorSpan.classList.add('hide');
            nextEvent();
          }
        }
      }
    });
  })();

  /**
   * Do not remove this section; it allows our team to troubleshoot and track feature adoption.
   * TS:0002-03-062
   */
</script>

Step 4.

Update the script with the ID of your ‘Previous’ and ‘Next’ button elements. Tip: Make sure you exclude the ‘#’ in the ID.

Step 5.

Copy this script and paste it into the Stylesheets section of your page:

<style>
  #container {
    margin-top: -45px;
    width: 100%;
    height: 8px;
    position: relative;
  }
  .lp-form-errors {
    display: none !important;
  }
  .hasError {
    border: 1px solid tomato !important;
    box-shadow: 0px 0px 5px 1px tomato !important;
  }
  .hide {
    display: none !important;
  }
  .lp-pom-form-field,
  .lp-pom-button {
    -webkit-animation: KEYFRAME-NAME 2s;
    -moz-animation: KEYFRAME-NAME 2s;
    -o-animation: KEYFRAME-NAME 2s;
    animation: KEYFRAME-NAME 2s;
  }

  @-webkit-keyframes KEYFRAME-NAME {
    0% {
      opacity: 0;
    }
    100% {
      opacity: 1;
    }
  }
  @-moz-keyframes KEYFRAME-NAME {
    0% {
      opacity: 0;
    }
    100% {
      opacity: 1;
    }
  }
  @-o-keyframes KEYFRAME-NAME {
    0% {
      opacity: 0;
    }
    100% {
      opacity: 1;
    }
  }
  @keyframes KEYFRAME-NAME {
    0% {
      opacity: 0;
    }
    100% {
      opacity: 1;
    }
  }
</style>

You’re done!

Now sit back and let the conversions roll in… :gif_master:


Can’t see the instructions? Log in or Join the Community to get access immediately. 🚀


Want to take your Unbounce landing pages + Convertables™ to the next level?
:spinbounce: Check out the Ultimate List of Unbounce Tips, Scripts & Hacks


224 replies

Thanks @Noah. Is there any way we can add 2-3 fields in the first “step”, and THEN go to the next set instead of doing it one by one?

Userlevel 7

hey @MaxG_BA, I think this is being caused by the auto-focus set up with this script.

To get rid of this, you can simply remove lines 26-28 of the script seen here: https://www.screencast.com/t/hZKYx2scOn2

Hope this helps!

Never mind, I solved the problem. If anyone else is curious to the solution just set a height/max height to the form fields container element. This contained the form within my content area and had no negative effects on its function.

I use a lot of multi step forms - the old fashion way with 2 different pages.
Yes I know - thats a rather complicated way to do the setup - however I really like that I have the data from step 1 even if the user wont fill out step 2.

Is this the case here as well?

Userlevel 7

Good news! I’ve added a built-in progress bar for the multistep form 2.0!

Get the latest version of the Javascript AND CSS here: https://gist.github.com/noahub/b8c6d63abb50048d42ee721c900450df

All you need to do is change the var showProgressBar to true. Give it a try and let me know what you think!

See it in action here: http://landingpage.noahmatsell.ca/multistep-form/

@aclayton @Andy2 I know you two were interested in this 🙂

Userlevel 7
Badge +3

Hi @RWF,

My team and I have done quite a few multi-step forms on Unbounce pages.

We’ve developed our own robust script to handle pretty complex multi-step forms which should be possible to apply to a convertable as well.

DM me a bit more details about your form and any special requirements you might have.

Best,
Hristian

Hello everyone,

I have dug and dug through this community trying to find a simple solution for a multi-step form that we could implement across multiple client pages. There are some good options, but I kept finding them to be a fit for one specific situation and not very flexible.

So my team and I spent about 15 working hours simplifying the code originally published in v1 of this thread and testing it to make sure it is workable across both pages and popups and fully allows the editing functions that we all love in Unbounce.

I don’t know if I’m allowed to post the blog article here or not, so I’m going to try, no subscription wall or bs, just simple steps to implement. I really hope this helps everyone. (It’s like a 2 minute read.)

How to build a multi-step form in Unbounce, instructions, downloadable docs and Q&A

I suddenly wish I’d learn some code instead and spoil the entire community here, the way @Noah does 🙂

This is fantastic. We have spent countless hours looking for a multi step form for Unbounce. We have tested Typeform and are currently testing an app called Leadformly which you can embed into unbounce pages. Of course you have to Zap your leads somewhere and you don’t get to use Unbounce A/B testing etc. so it’s not great.

Where is your native multi step form builder Unbounce?

Thanks Noah,

This is an excellent script. We implemented it on our landing page. But we have a lot of form fields. We wanted to know if we can group the personal details fields (Name, Phone Number & Email) in one group to show together when the user clicks on Next -> then we show the Company Details fields together.
So the next button in all appears twice only.

Userlevel 7

Hey Nicholas, this should do the trick!

Question!

Is there a way to have 3 imput fields at a time instead just 1? My form has 9 imput fields and I need to show 3 fields divided in 3 sections. Is this possible?

Also, can I change the progress bar? and how to do it?

Unfortunately I can’t share with you the live url because this page is still in edition progress, no live link yet, but it looks like this:

I need something like this example, 3 sections with 3 imput fileds on it and hopefully with the same progress bar:

mstp

BR,

Andres V.

Userlevel 2
Badge

@Noah Thanks for the wonderful script. I have seen huge number of people asking for Multiple fields
most probably Name, Email and Phone Number to be displayed in one step. Is there something you could think of to make this happen?

I would be really happy to see a new script to have such functionality because one script will resolve a problem for hundreds of people.

Can you please let me know?

I’ve got a question which @mike22rtn touched on …displaying a group of questions (like maybe a fieldset or something).
While a Conversation Form would be my first choice, this Multi-Step for (with progress bar) is a very close second.

Has @Noah or anyone discovered a way to display multiple questions at once?

Here’s the use case.
We need the visitors height in inches.
Obviously, people would rather enter feet and inches, and just have us do the math for them.

We would LOVE to do so, but I’m not seeing an easy way to achieve that.
What is an easy (user friendly) way to collect two related fields at once with the Multi-step form?

Hey

Can someone tel me how to add the progress bar like the demo it isnt showing on my page

Thank you

@Caroline This is pretty awesome. I’m trying it out with a sandbox form to get a feel for the script etc.

I noticed in the code you have line 14 lp.jQuery('#progress-bar').hide(); when I changed it to show I got 3 numbers. Is there some styling that I’m missing or is this not the kind of progress bar I am thinking about?

I’m going to try and apply some inline validation and error handling to the form… wish me luck!

Joe

Ahhh. I see. No, you create the back and next buttons yourself. You can style them however you like.

When you create the two buttons you have to find their ID’s & make sure that is what’s in your script.

So here’s my back button :

back%20button
back button.PNG966x305 86.8 KB

Here’s my next button:

next%20button
next button.PNG848x274 47.6 KB

And you can see I’ve put those in the script here:

multi-step%20script
multi-step script.PNG977x127 7.61 KB

This is what it looks like when it’s up and running:
multi-step%20styling

I’ve just pointed out a couple of other things I’ve done.

I’ve but a box around the progress bar and I’ve changed the colour of the step counter to match the styling of my page. The box is as simple as creating it and arranging it behind the form in the right spot.

Changing the colour is in the script. It’s here:step%20counter%20text

In that picture you can see that you can also change the progress bar colour (the from: & the to: values).

Userlevel 7
Badge +1

What a beaut! :noah:

Brilliant 🙂

Had sent briefs to developers to do this - now no need - thanks very much 🙂

Userlevel 7

@Mark_H + @inboundbear this solution won’t record data after each step–it simply splits a single form into multiple steps and submits the data at the end.

Userlevel 7
Badge +1

Hey Ben!

Didn’t want to leave your question hanging here, especially because I’m sure there are some other folks wondering the same thing.

To be completely transparent - the code for our forms is some of the oldest code that exists in our entire product. Tampering with them is an enormous undertaking, and at this point in time our focus has been on different improvements (Convertables, Bulk Actions, etc.). It’s for this reason that we share these types of Tips & Scripts to give you all some options in the meantime before we start updating our native features.

Hopefully this provides a little bit of insight into this situation for now. Please keep the feedback and questions coming! And I’m glad to hear you’re liking this workaround so far. 🙂

Happy Friday! 🍻

Userlevel 7

I just visited http://ar-vida.primerolafamilia.com.ar/seguro_vida1/ and it looks like it’s no longer focusing on load. If you’re still seeing the focus on load you may need to clear your cache to see the most recent version of your page.

Userlevel 7

Quick Update:

The latest version of this script (find it here) now automatically enables inline validation! Here’s an example:

Enjoy 😘

Hey guys!

We have seen first hand that multi-step forms are a powerful tactic to increasing conversion rates, especially if you can get the user to invest themselves in the first couple of fields (often asking the user for the less commitment based fields first).

With that in mind, is there anyway to display more than 1 field at a time? As it stands right now I can’t see this feature working for much of our clients as one of the primary reasons for using a multi-step field is to break down the form into manageable chunks. For instance, If we had 9 fields to capture from the user that would require them to click the next button 9 times.

I’m excited to see how this feature develops!

Hi Noah,
Thanks so much for fixing the bug, the drop down menu is now working as
well. If we get the progress bar and possibility of showing more than one field
at the time, that would be great!

Reply