[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

Awesome, thanks a lot!

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 🙂

Badge

Amazing!! Thanks very much @Noah!! Can’t wait to give this a try.

@Noah, Thank you so much for implementing this. I can confirm that it works
and the bar changes color to darker green as we get closer to the finish
line!

Now only if we had the option of displaying multiple fields at once. That
would be a dream come true!

Regards,

Badge

@Samir And capability to use field types other than single line text!

I have other field types such as larger fields and drop down and all works fine. @Andy2

Badge

Hmmm, @Noah any thoughts on this? I was working with Kathryn from support on this, she said that you told her only single text lines could work with this script.

(Also, I’m going to get greedy now… any way to change placement of progress bar?? For example, how high it shows above form? Or maybe place it below form?)

Thanks Noah, this is great! Looking forward to trying this 🙂

Userlevel 7

hey @Andy2, what do you mean by single text line? Are you having issues with a multiline textarea field?

And to change the placement of the progress bar, try adjusting the CSS styling for #container (the margin-top value).

Badge

@Noah I had tried to use a drop-down as one of my steps, and it wasn’t working. Kathryn from support told me that this was a known limitation of this current code, that only single text lines could work. But maybe I misunderstood??

And thanks for tip for placement of progress bar, that worked a treat!

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?

Badge

@Noah is it possible to disable the script for multistep form on desktop, and only show on mobile? Or, use two different scripts for desktop and mobile?

Userlevel 7

Hi @Andy2,

You may have some luck wrapping this code in a conditional statement based on window size:

<script>
  if ( lp.jQuery(window).width() <= 600 ) {
    // Your mobile code goes here
  }else{
    // Your desktop code goes here
  }
</script> 

Hope this helps point you in the right direction!

Badge

Thanks @Noah! I’m testing out now, seems promising. At least one issue I’m encountering with this solution is that validation no longer seems to work.

Badge

@Noah I’ve tested this out here: http://unbouncepages.com/multistep-mobile-only/

On desktop, the validation works, but error messages aren’t appearing. On mobile, validation and error messages seem to be working fine.

Any thoughts on how to get error messages to appear on desktop? Thanks!

Userlevel 7

Hey Andy, you’ll likely have to write a media query that only applies the following CSS to a max-width of 600px:


Here’s a guide on media queries if you need any help: https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries

I hope that does the trick!

Badge

Thanks very much, that did the trick!!

(P.S. Wondering if anyone else is seeing varying results with the multistep form on desktop vs mobile? We’ve found that it helps on mobile but hurts on desktop.)

Hi All,

I love, love, love this form. This is exactly what we have been after, the multiple page approach has been painful on mobile due to having to load pages but i’m sure this will fix that.

Apologies if this has already been mentioned but i’m struggling to figure out how to put multiple fields in each stage. i.e. First Name and Last Name in the same stage rather than one after the other. Any guidance?

Also, is it possible to make the first button stretch the length of the form?

Thanks

@Noah is there any possible way to combine your awesome script with your other script, the Form Glow Script? They are both awesome, and I’d love to use them simultaneously.

On another note, I saw @Samir talk about drop-down menu’s. I haven’t been able to fix this for myself yet, the drop-downs won’t allow me to continue filling in the multi-step form. Is there a solution for this yet?

Kind regards,

Pepijn

Isn’t the first button all by your own design? You can make it as long as you’d like, or am I seeing this wrong?

It seems like previous code was a bit more simple with being able to decide which fields go into each section as below.

function multistepForm() { **var pages = {** ** 1: '#container_type',** ** 2: '#container_first_name, #container_last_name',** ** 3: '#container_email_address, #container_phone_home'** }; var headers = { 1: '#lp-pom-text-1095, #lp-pom-text-1100', 2: '#lp-pom-text-1096, #lp-pom-text-1098', 3: '#lp-pom-text-1097, #lp-pom-text-1099' I cant seem to find the equivalent in the new code.

Hi community-members,
I am running into the following problem: I want to integrate the multistep-form into a convertable. This works perfectly fine for desktop. However, for mobile I got a very cluttered view on Chrome browser and no view at all on Safari.

Has anybody an idea to solve this?

The biggest part of our traffic is mobile.

Best regards,
Nicol from OneFit

Userlevel 7

For the Safari issue I think this may have been related to the use of an arrow function, which is not fully supported by all browsers. I’ve replaced this with a regular function and it appears to have resolved the issue I was seeing in my testing. Give the latest script a try and let me know how it goes: https://gist.github.com/noahub/b8c6d63abb50048d42ee721c900450df3

Userlevel 7
Badge +4

Hey Nicole!

Could you share a link/screenshot?

Cheers 🙂

Hi Noah,

I’m having an issue with the pages that have this script on them opening straight to the form rather than the top of the page. I can work around it by adding the ID of the top most section to the URL, but I was wondering if you knew why this was happening? I can send you a link to one of the pages this happens on if you want.

Thanks,

Alice

Reply