Giter VIP home page Giter VIP logo

multi-step-form-with-validation's Introduction

Multi step form with per-step validation

Example Webflow site: https://multi-step-form-and-validation.webflow.io/

form example in Webflow

How it works

Only one Webflow form element is used. Every form step is organized into a single div block called step.

form steps in the navigator panel

The total number of form steps depends on how many step elements there are. This example has 4 steps.

Each step contains a grouping of the

  • input label
  • input(s)
  • error message

input group in each form step

This structure allows us to show an error/success for each input we're validating by passing the input to the setError or setSuccess functions.

When validation fails, the input-error class is added to the input to give it a red color and an 'x' icon, indicating an error.

When validation passes, the input-success class is added to the input giving it a green color and checkmark, indicating it has passed.

input group in each form step

By default, all form steps are set to display none except for the first step. The active class is added to the relevant step to show it. The step can only be changed forwards if validation in the current step passes. Going back doesn't require validation. The active class is added to the first step in Webflow to show it when the page loads.

The current step is shown with the steps overview element.

input group in each form step

The Webflow.push function handles validation for the last step because it submits the form. See the form validation repo to learn about validating a form before submission.

The Javascript

The code begins by getting neccessary elements like the form, the total steps in the form, and the form overview steps.

Next we set the currentstep to 0 which indicates the first step.

Next get all of the inputs for each step.

Since we're using radio buttons and checkboxes, we set up event listeners to determine when each is checked. Those functions are instantiated.

Next we set up a click listener on the form element. In this function, we return if a previous or next button is not clicked. Meaning we don't perform any validation or step change logic unless the user clicks on previous or next.

Based on which button is clicked — previous or next — we perform the relevant logic for validation and change the form step if validation passes.

Next is a series of function definitions for showing the current step, going to the next step, handling the step overview element, setting an input error, and an input success.

Next is a series of functions to handle the validation for each form step — which we call in the click listener on the form element.

Next we have the Webflow.push function to handle validation on the last step when the user hits submit.

Next are function definitions for the radio and checkbox event listeners.

Next are blur event listeners on the name, email, and phone inputs to also validate when the user moves off the input.

Next are input event listeners to validate as the user is typing, only when there is an input error.

Finally we have the individual function definitions for validating the name, email, and phone number inputs. This is where you would include your unique logic for each input. These functions are used in each of the relevant validation steps. They return false when validation fails which prevents the form from progressing to the next step.

const form = document.getElementById("form");
const formSteps = [...document.getElementsByClassName("step")];
const stepsOverview = [...document.getElementsByClassName("form-step")];

// set current step to start form
let currentStep = 0;

// step 1 - inputs
const userName = document.getElementById("name");
const email = document.getElementById("email");

// step 2 - inputs
const serviceCheckboxes = document.querySelectorAll(`input[type="checkbox"]`);

// step 3 - inputs
const budgetRadios = document.querySelectorAll(`input[name="budget"]`);

// step 4 - inputs (final)
const phone = document.getElementById("phone");
const company = document.getElementById("company");

// initiate radio and checkbox listeners for steps 2 and 3
addRadioListeners();
addCheckboxListeners();

// listen for click in form
// only continue if click is on a next or previous button
form.addEventListener("click", (e) => {
  const next = e.target.classList.contains("next");
  const previous = e.target.classList.contains("previous");

  if (!next && !previous) return;

  if (previous) {
    currentStep -= 1;
    handleCurrentStep(currentStep);
    showCurrentStep();
    return;
  }

  // no need to call last step, as that is called through the Webflow.push function on page load, found below
  switch (true) {
    case currentStep === 0:
      validateStepOne();
      break;
    case currentStep === 1:
      validateStepTwo();
      break;
    case currentStep === 2:
      validateStepThree();
      break;
    default:
      break;
  }
});

// show current step in form
function showCurrentStep() {
  formSteps.forEach((step, index) => {
    step.classList.toggle("active", index === currentStep);
  });
}

// go to next step
function goToNextStep() {
  currentStep += 1;
  handleCurrentStep(currentStep);
  showCurrentStep();
}

// handle current step
function handleCurrentStep(liveStep) {
  stepsOverview.forEach((step, index) => {
    if (liveStep === index) {
      step.classList.add("current-step");
    } else {
      step.classList.remove("current-step");
    }
  });
}

// set error in each step
const setError = (element, message) => {
  const inputControl = element.parentElement;
  const errorMessage = inputControl.querySelector(".error-message");

  errorMessage.innerText = message;
  errorMessage.style.display = "block";

  if (
    element?.childNodes[0]?.type === "radio" ||
    element?.childNodes[0]?.type === "checkbox"
  )
    return;

  // only change input if text, email, textarea, phone, or number
  element.classList.add("input-error");
  element.classList.remove("input-success");
};

// set success in each step and remove error message
const setSuccess = (element) => {
  const inputControl = element.parentElement;
  const errorMessage = inputControl.querySelector(".error-message");

  errorMessage.innerText = "";
  errorMessage.style.display = "none";
  element.classList.remove("input-error");

  if (
    element?.childNodes[0]?.type === "radio" ||
    element?.childNodes[0]?.type === "checkbox"
  )
    return;

  // only change input if text, email, textarea, phone, number
  element.classList.add("input-success");
};

// validation steps
function validateStepOne() {
  const checkName = validateNameInput();
  const checkEmail = validateEmailInput();

  if (!checkName || !checkEmail) return;

  goToNextStep();
}

function validateStepTwo() {
  const checkboxes = [...document.querySelectorAll('input[type="checkbox"]')];
  const checkAtLeastOne = checkboxes.some((input) => input.checked);

  if (checkAtLeastOne) {
    goToNextStep();
    setSuccess(budgetRadios[0].parentElement);
  } else {
    setError(checkboxes[0].parentElement, "Select at least one option");
  }
}

function validateStepThree() {
  const isItChecked = document.querySelector('input[name="budget"]:checked');

  if (isItChecked !== null) {
    setSuccess(budgetRadios[0].parentElement);
    goToNextStep();
    return;
  } else {
    setError(budgetRadios[0].parentElement, "Select a budget");
  }
}

// last step validation on form submit
Webflow.push(function () {
  $("form").submit(function () {
    const checkNumber = validatePhoneInput();
    if (!checkNumber) return false;
  });
});

// event listeners
function addRadioListeners() {
  for (radio in budgetRadios) {
    budgetRadios[radio].onclick = function () {
      if (this.value) {
        setSuccess(budgetRadios[0].parentElement);
      }
    };
  }
}

function addCheckboxListeners() {
  for (checkbox in serviceCheckboxes) {
    serviceCheckboxes[checkbox].onclick = function () {
      if (this.value) {
        setSuccess(serviceCheckboxes[0].parentElement);
      }
    };
  }
}

// validation on blur
userName.addEventListener("blur", validateNameInput);
email.addEventListener("blur", validateEmailInput);
phone.addEventListener("blur", validatePhoneInput);

// validation on keystroke only if input error class is active
userName.addEventListener(
  "input",
  () => inputHasError(userName) && validateNameInput()
);
phone.addEventListener(
  "input",
  () => inputHasError(phone) && validatePhoneInput()
);
email.addEventListener(
  "input",
  () => inputHasError(email) && validateEmailInput()
);

// utility functions to check if input has the input error class
function inputHasError(input) {
  return input.classList.contains("input-error");
}

// validation functions
function validateNameInput() {
  if (userName.value.trim() === "") {
    setError(userName, "Name is required");
    return false;
  } else if (userName.value.trim().length < 3) {
    setError(userName, "Name must be 3 characters or longer");
    return false;
  } else {
    setSuccess(userName);
    return true;
  }
}

function validateEmailInput() {
  const emailPattern =
    /^(([^<>()[\]\.,;:\s@\"]+(\.[^<>()[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
  const isEmailValid = emailPattern.test(email.value);

  if (!isEmailValid) {
    setError(email, "Enter a valid email address i.e., [email protected]");
    return false;
  } else {
    setSuccess(email);
    return true;
  }
}

function validatePhoneInput() {
  const phonePattern = /^[0-9]*$/;
  const isPhoneValid = phonePattern.test(phone.value);

  if (!isPhoneValid) {
    setError(phone, "Enter numbers only");
    return false;
  } else if (phone.value.length < 10) {
    setError(phone, "Phone number must be 10 digits");
    return false;
  } else {
    setSuccess(phone);
    return true;
  }
}

multi-step-form-with-validation's People

Contributors

wadoodh avatar

Stargazers

 avatar

Watchers

 avatar

multi-step-form-with-validation's Issues

Require email validation?

Hi! I'm starting to look into Webflow app development and this looks like a great boilerplate.

Question: how could one add email verification to this to ensure the email exists?

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.