Animated Multi-step Form For Bootstrap

Category: Form , Javascript | October 2, 2019
Author: nat-davydova
Views Total: 429
Official Page: Go to website
Last Update: October 2, 2019
License: MIT

Preview:

Animated Multi-step Form For Bootstrap

Description:

A JavaScript and CSS extension of Bootstrap 4 that lets you create a multi-step form with custom CSS3 animations.

Without the need of jQuery library and Bootstrap JS.

How to use it:

Include the Bootstrap’s stylesheet on the page.

<script src="/path/to/bootstrap.min.css"></script>

Add form panels together with a progress bar to the multi-step form as shown below:

<div class="multisteps-form">
  <!--progress bar-->
  <div class="row">
    <div class="col-12 col-lg-8 ml-auto mr-auto mb-4">
      <div class="multisteps-form__progress">
        <button class="multisteps-form__progress-btn js-active" type="button" title="User Info">User Info</button>
        <button class="multisteps-form__progress-btn" type="button" title="Address">Address</button>
        <button class="multisteps-form__progress-btn" type="button" title="Order Info">Order Info</button>
        <button class="multisteps-form__progress-btn" type="button" title="Comments">Comments        </button>
      </div>
    </div>
  </div>
  <!--form panels-->
  <div class="row">
    <div class="col-12 col-lg-8 m-auto">
      <form class="multisteps-form__form">
        <!--single form panel-->
        <div class="multisteps-form__panel shadow p-4 rounded bg-white js-active" data-animation="scaleIn">
          <h3 class="multisteps-form__title">Your User Info</h3>
          <div class="multisteps-form__content">
            <div class="form-row mt-4">
              <div class="col-12 col-sm-6">
                <input class="multisteps-form__input form-control" type="text" placeholder="First Name"/>
              </div>
              <div class="col-12 col-sm-6 mt-4 mt-sm-0">
                <input class="multisteps-form__input form-control" type="text" placeholder="Last Name"/>
              </div>
            </div>
            <div class="form-row mt-4">
              <div class="col-12 col-sm-6">
                <input class="multisteps-form__input form-control" type="text" placeholder="Login"/>
              </div>
              <div class="col-12 col-sm-6 mt-4 mt-sm-0">
                <input class="multisteps-form__input form-control" type="email" placeholder="Email"/>
              </div>
            </div>
            <div class="form-row mt-4">
              <div class="col-12 col-sm-6">
                <input class="multisteps-form__input form-control" type="password" placeholder="Password"/>
              </div>
              <div class="col-12 col-sm-6 mt-4 mt-sm-0">
                <input class="multisteps-form__input form-control" type="password" placeholder="Repeat Password"/>
              </div>
            </div>
            <div class="button-row d-flex mt-4">
              <button class="btn btn-primary ml-auto js-btn-next" type="button" title="Next">Next</button>
            </div>
          </div>
        </div>
        <!--single form panel-->
        <div class="multisteps-form__panel shadow p-4 rounded bg-white" data-animation="scaleIn">
          <h3 class="multisteps-form__title">Your Address</h3>
          <div class="multisteps-form__content">
            <div class="form-row mt-4">
              <div class="col">
                <input class="multisteps-form__input form-control" type="text" placeholder="Address 1"/>
              </div>
            </div>
            <div class="form-row mt-4">
              <div class="col">
                <input class="multisteps-form__input form-control" type="text" placeholder="Address 2"/>
              </div>
            </div>
            <div class="form-row mt-4">
              <div class="col-12 col-sm-6">
                <input class="multisteps-form__input form-control" type="text" placeholder="City"/>
              </div>
              <div class="col-6 col-sm-3 mt-4 mt-sm-0">
                <select class="multisteps-form__select form-control">
                  <option selected="selected">State...</option>
                  <option>...</option>
                </select>
              </div>
              <div class="col-6 col-sm-3 mt-4 mt-sm-0">
                <input class="multisteps-form__input form-control" type="text" placeholder="Zip"/>
              </div>
            </div>
            <div class="button-row d-flex mt-4">
              <button class="btn btn-primary js-btn-prev" type="button" title="Prev">Prev</button>
              <button class="btn btn-primary ml-auto js-btn-next" type="button" title="Next">Next</button>
            </div>
          </div>
        </div>
        <!--single form panel-->
        <div class="multisteps-form__panel shadow p-4 rounded bg-white" data-animation="scaleIn">
          <h3 class="multisteps-form__title">Your Order Info</h3>
          <div class="multisteps-form__content">
            <div class="row">
              <div class="col-12 col-md-6 mt-4">
                <div class="card shadow-sm">
                  <div class="card-body">
                    <h5 class="card-title">Item Title</h5>
                    <p class="card-text">Small and nice item description</p><a class="btn btn-primary" href="#" title="Item Link">Item Link</a>
                  </div>
                </div>
              </div>
              <div class="col-12 col-md-6 mt-4">
                <div class="card shadow-sm">
                  <div class="card-body">
                    <h5 class="card-title">Item Title</h5>
                    <p class="card-text">Small and nice item description</p><a class="btn btn-primary" href="#" title="Item Link">Item Link</a>
                  </div>
                </div>
              </div>
            </div>
            <div class="row">
              <div class="button-row d-flex mt-4 col-12">
                <button class="btn btn-primary js-btn-prev" type="button" title="Prev">Prev</button>
                <button class="btn btn-primary ml-auto js-btn-next" type="button" title="Next">Next</button>
              </div>
            </div>
          </div>
        </div>
        <!--single form panel-->
        <div class="multisteps-form__panel shadow p-4 rounded bg-white" data-animation="scaleIn">
          <h3 class="multisteps-form__title">Additional Comments</h3>
          <div class="multisteps-form__content">
            <div class="form-row mt-4">
              <textarea class="multisteps-form__textarea form-control" placeholder="Additional Comments and Requirements"></textarea>
            </div>
            <div class="button-row d-flex mt-4">
              <button class="btn btn-primary js-btn-prev" type="button" title="Prev">Prev</button>
              <button class="btn btn-success ml-auto" type="button" title="Send">Send</button>
            </div>
          </div>
        </div>
      </form>
     </div>
  </div>
</div>

The necessary CSS styles for the multi-step form.

.multisteps-form__progress {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
}

.multisteps-form__progress-btn {
  transition-property: all;
  transition-duration: 0.15s;
  transition-timing-function: linear;
  transition-delay: 0s;
  position: relative;
  padding-top: 20px;
  color: rgba(108, 117, 125, 0.7);
  text-indent: -9999px;
  border: none;
  background-color: transparent;
  outline: none !important;
  cursor: pointer;
}

@media (min-width: 500px) {
  .multisteps-form__progress-btn {
    text-indent: 0;
  }
}

.multisteps-form__progress-btn:before {
  position: absolute;
  top: 0;
  left: 50%;
  display: block;
  width: 13px;
  height: 13px;
  content: '';
  -webkit-transform: translateX(-50%);
          transform: translateX(-50%);
  transition: all 0.15s linear 0s, -webkit-transform 0.15s cubic-bezier(0.05, 1.09, 0.16, 1.4) 0s;
  transition: all 0.15s linear 0s, transform 0.15s cubic-bezier(0.05, 1.09, 0.16, 1.4) 0s;
  transition: all 0.15s linear 0s, transform 0.15s cubic-bezier(0.05, 1.09, 0.16, 1.4) 0s, -webkit-transform 0.15s cubic-bezier(0.05, 1.09, 0.16, 1.4) 0s;
  border: 2px solid currentColor;
  border-radius: 50%;
  background-color: #fff;
  box-sizing: border-box;
  z-index: 3;
}

.multisteps-form__progress-btn:after {
  position: absolute;
  top: 5px;
  left: calc(-50% - 13px / 2);
  transition-property: all;
  transition-duration: 0.15s;
  transition-timing-function: linear;
  transition-delay: 0s;
  display: block;
  width: 100%;
  height: 2px;
  content: '';
  background-color: currentColor;
  z-index: 1;
}

.multisteps-form__progress-btn:first-child:after {
  display: none;
}

.multisteps-form__progress-btn.js-active {
  color: #007bff;
}

.multisteps-form__progress-btn.js-active:before {
  -webkit-transform: translateX(-50%) scale(1.2);
          transform: translateX(-50%) scale(1.2);
  background-color: currentColor;
}

.multisteps-form__form {
  position: relative;
}

.multisteps-form__panel {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 0;
  opacity: 0;
  visibility: hidden;
}

.multisteps-form__panel.js-active {
  height: auto;
  opacity: 1;
  visibility: visible;
}

Define your own CSS3 animations in the CSS.

.multisteps-form__panel[data-animation="scaleIn"] {
  -webkit-transform: scale(0.9);
          transform: scale(0.9);
}

.multisteps-form__panel[data-animation="scaleIn"].js-active {
  transition-property: all;
  transition-duration: 0.2s;
  transition-timing-function: linear;
  transition-delay: 0s;
  -webkit-transform: scale(1);
          transform: scale(1);
}

The main JavaScript to activate the multi-step form.

//DOM elements
const DOMstrings = {
  stepsBtnClass: 'multisteps-form__progress-btn',
  stepsBtns: document.querySelectorAll(`.multisteps-form__progress-btn`),
  stepsBar: document.querySelector('.multisteps-form__progress'),
  stepsForm: document.querySelector('.multisteps-form__form'),
  stepsFormTextareas: document.querySelectorAll('.multisteps-form__textarea'),
  stepFormPanelClass: 'multisteps-form__panel',
  stepFormPanels: document.querySelectorAll('.multisteps-form__panel'),
  stepPrevBtnClass: 'js-btn-prev',
  stepNextBtnClass: 'js-btn-next' };


//remove class from a set of items
const removeClasses = (elemSet, className) => {

  elemSet.forEach(elem => {

    elem.classList.remove(className);

  });

};

//return exect parent node of the element
const findParent = (elem, parentClass) => {

  let currentNode = elem;

  while (!currentNode.classList.contains(parentClass)) {
    currentNode = currentNode.parentNode;
  }

  return currentNode;

};

//get active button step number
const getActiveStep = elem => {
  return Array.from(DOMstrings.stepsBtns).indexOf(elem);
};

//set all steps before clicked (and clicked too) to active
const setActiveStep = activeStepNum => {

  //remove active state from all the state
  removeClasses(DOMstrings.stepsBtns, 'js-active');

  //set picked items to active
  DOMstrings.stepsBtns.forEach((elem, index) => {

    if (index <= activeStepNum) {
      elem.classList.add('js-active');
    }

  });
};

//get active panel
const getActivePanel = () => {

  let activePanel;

  DOMstrings.stepFormPanels.forEach(elem => {

    if (elem.classList.contains('js-active')) {

      activePanel = elem;

    }

  });

  return activePanel;

};

//open active panel (and close unactive panels)
const setActivePanel = activePanelNum => {

  //remove active class from all the panels
  removeClasses(DOMstrings.stepFormPanels, 'js-active');

  //show active panel
  DOMstrings.stepFormPanels.forEach((elem, index) => {
    if (index === activePanelNum) {

      elem.classList.add('js-active');

      setFormHeight(elem);

    }
  });

};

//set form height equal to current panel height
const formHeight = activePanel => {

  const activePanelHeight = activePanel.offsetHeight;

  DOMstrings.stepsForm.style.height = `${activePanelHeight}px`;

};

const setFormHeight = () => {
  const activePanel = getActivePanel();

  formHeight(activePanel);
};

//STEPS BAR CLICK FUNCTION
DOMstrings.stepsBar.addEventListener('click', e => {

  //check if click target is a step button
  const eventTarget = e.target;

  if (!eventTarget.classList.contains(`${DOMstrings.stepsBtnClass}`)) {
    return;
  }

  //get active button step number
  const activeStep = getActiveStep(eventTarget);

  //set all steps before clicked (and clicked too) to active
  setActiveStep(activeStep);

  //open active panel
  setActivePanel(activeStep);
});

//PREV/NEXT BTNS CLICK
DOMstrings.stepsForm.addEventListener('click', e => {

  const eventTarget = e.target;

  //check if we clicked on `PREV` or NEXT` buttons
  if (!(eventTarget.classList.contains(`${DOMstrings.stepPrevBtnClass}`) || eventTarget.classList.contains(`${DOMstrings.stepNextBtnClass}`)))
  {
    return;
  }

  //find active panel
  const activePanel = findParent(eventTarget, `${DOMstrings.stepFormPanelClass}`);

  let activePanelNum = Array.from(DOMstrings.stepFormPanels).indexOf(activePanel);

  //set active step and active panel onclick
  if (eventTarget.classList.contains(`${DOMstrings.stepPrevBtnClass}`)) {
    activePanelNum--;

  } else {

    activePanelNum++;

  }

  setActiveStep(activePanelNum);
  setActivePanel(activePanelNum);

});

//SETTING PROPER FORM HEIGHT ONLOAD
window.addEventListener('load', setFormHeight, false);

//SETTING PROPER FORM HEIGHT ONRESIZE
window.addEventListener('resize', setFormHeight, false);

You Might Be Interested In:


Leave a Reply