How to make multistep form using HTML, CSS, JS

Piyush608 top freelancer india 6

Creating a multistep form involves structuring your HTML for multiple steps, using CSS for styling, and JavaScript to handle the navigation between steps. Here’s a basic example to get you started:

HTML

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Multistep Form</title>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js" defer></script>
  </head>
  <body>
    <form class="form-wizard">
      <!-- .completed -->
      <div class="completed" hidden>
        <svg
          xmlns="http://www.w3.org/2000/svg"
          fill="none"
          viewBox="0 0 24 24"
          stroke-width="1.5"
          stroke="currentColor"
        >
          <path
            stroke-linecap="round"
            stroke-linejoin="round"
            d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
          />
        </svg>

        <h3>Registration Successful!</h3>
        <p>Your account has been created.</p>
      </div>
      <!-- /.completed -->

      <h1>Registration</h1>

      <!-- .progress-container -->
      <div class="progress-container">
        <div class="progress"></div>

        <ol>
          <li class="current">Personal Info</li>
          <li>Contact Info</li>
          <li>Account Info</li>
          <li>About</li>
        </ol>
      </div>
      <!-- /.progress-container -->

      <!-- .steps-container -->
      <div class="steps-container">
        <div class="step">
          <h3>Personal Information</h3>

          <div class="form-control">
            <label for="first-name">First Name</label>
            <input type="text" id="first-name" name="first-name" required />
          </div>
          <div class="form-control">
            <label for="last-name">Last Name</label>
            <input type="text" id="last-name" name="last-name" required />
          </div>
        </div>
        <div class="step">
          <h3>Contact Information</h3>

          <div class="form-control">
            <label for="email">Email</label>
            <input type="email" id="email" name="email" required />
          </div>

          <div class="form-control">
            <label for="phone">Phone</label>
            <input type="tel" id="phone" name="phone" required />
          </div>
        </div>
        <div class="step">
          <h3>Account Information</h3>

          <div class="form-control">
            <label for="username">Username</label>
            <input type="text" id="username" name="username" required />
          </div>
          <div class="form-control">
            <label for="password">Password</label>
            <input type="password" id="password" name="password" required />
          </div>
          <div class="form-control">
            <label for="confirm-password">Confirm Password</label>
            <input
              type="password"
              id="confirm-password"
              name="confirm-password"
              required
            />
          </div>
        </div>
        <div class="step">
          <h3>About</h3>

          <div class="form-control">
            <label for="bio">Bio</label>
            <textarea name="bio" id="bio" rows="4" required></textarea>
          </div>
        </div>
      </div>
      <!-- /.steps-container -->

      <!-- .controls -->
      <div class="controls">
        <button type="button" class="prev-btn">Prev</button>
        <button type="button" class="next-btn">Next</button>
        <button type="submit" class="submit-btn">Submit</button>
      </div>
      <!-- /.controls -->
    </form>
  </body>
</html>

 

CSS (styles.css)

@import url("https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600&display=swap");

* {
  font-family: "Outfit", sans-serif;
  padding: 0;
  margin: 0;
  box-sizing: border-box;
}

:root {
  --main-color: #c41e67;
  --dark-color: #a71555;
  --step-color: #ced7e0;
  --step-size: 32px;
  --steps: 4;
  --progress-width: calc((var(--steps) - 1) / var(--steps) * 100%);
}

body {
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: lavender;
  color: #333;
  overflow: hidden;
}

h1 {
  text-align: center;
  margin-bottom: 16px;
}

h3 {
  margin-bottom: 16px;
}

label {
  display: inline-block;
  margin-bottom: 4px;
}

input,
textarea {
  outline-color: var(--main-color);
  border: 1px solid lightgray;
  padding: 8px 16px;
  border-radius: 4px;
  width: 100%;
  font-size: 16px;
}

textarea {
  resize: none;
}

button {
  cursor: pointer;
  border: none;
  border-radius: 16px;
  padding: 8px 16px;
  background-color: var(--main-color);
  color: #fff;
  font-size: 14px;
  transition: 0.3s;
}

button:active,
button:hover {
  background-color: var(--dark-color);
}

button:disabled {
  opacity: 0.8;
}

button:focus {
  outline-color: #333;
}

button[type="submit"] {
  background-color: #333;
}

button[type="submit"]:hover,
button[type="submit"]:active {
  background-color: #444;
}

/* form styles */

.form-wizard {
  background-color: #fff;
  padding: 32px;
  border-radius: 8px;
  width: min(600px, 80%);
  box-shadow: 0 4px 16px rgba(167, 21, 84, 0.1);
}

.progress-container {
  position: relative;
}

.progress-container::before {
  content: "";
  height: 4px;
  width: var(--progress-width);
  background-color: var(--step-color);
  position: absolute;
  top: calc(var(--step-size) / 2);
  left: 50%;
  translate: -50% -50%;
}

.progress-container .progress {
  height: 4px;
  width: var(--progress-width);
  background-color: var(--main-color);
  position: absolute;
  top: calc(var(--step-size) / 2);
  left: 50%;
  translate: -50% -50%;
  z-index: 2;
  transform: scaleX(0);
  transform-origin: left;
  transition: 0.3s;
}

.progress-container ol {
  list-style-type: none;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  counter-reset: step-number;
  position: relative;
  z-index: 3;
}

.progress-container li {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  color: var(--step-color);
  transition: 0.3s linear 0.3s;
  text-align: center;
}

.progress-container li::before {
  counter-increment: step-number;
  content: counter(step-number);
  background-color: var(--step-color);
  width: var(--step-size);
  height: var(--step-size);
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  color: #fff;
  transition: 0.3s linear 0.3s;
}

.progress-container li:where(.done, .current) {
  color: #333;
  font-weight: 500;
}

.progress-container li.done::before {
  background-color: var(--main-color);
}

.progress-container li.current::before {
  background-color: var(--main-color);
  box-shadow: 0 0 0 3px rgba(167, 21, 84, 0.25);
}

.steps-container {
  display: flex;
  overflow: hidden;
}

.step {
  flex: 1 0 100%;
  padding: 24px 8px;
  opacity: 0;
  transition: opacity 0.3s;
  height: fit-content;
  display: grid;
  gap: 8px;
}

.step.current {
  opacity: 1;
}

.controls {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-top: 16px;
  padding-inline: 8px;
}

.controls button {
  flex: 1;
}

.completed {
  text-align: center;
}

.completed h3 {
  margin-bottom: 8px;
}

.completed svg {
  width: 100px;
  height: 100px;
  stroke: yellowgreen;
}

.completed:not([hidden]) ~ * {
  display: none;
}

 

JavaScript (script.js)

document.addEventListener("DOMContentLoaded", () => {
  const form = document.querySelector(".form-wizard");
  const progress = form.querySelector(".progress");
  const stepsContainer = form.querySelector(".steps-container");
  const steps = form.querySelectorAll(".step");
  const stepIndicators = form.querySelectorAll(".progress-container li");
  const prevButton = form.querySelector(".prev-btn");
  const nextButton = form.querySelector(".next-btn");
  const submitButton = form.querySelector(".submit-btn");

  document.documentElement.style.setProperty("--steps", stepIndicators.length);

  let currentStep = 0;

  const updateProgress = () => {
    let width = currentStep / (steps.length - 1);
    progress.style.transform = `scaleX(${width})`;

    stepsContainer.style.height = steps[currentStep].offsetHeight + "px";

    stepIndicators.forEach((indicator, index) => {
      indicator.classList.toggle("current", currentStep === index);
      indicator.classList.toggle("done", currentStep > index);
    });

    steps.forEach((step, index) => {
      step.style.transform = `translateX(-${currentStep * 100}%)`;
      step.classList.toggle("current", currentStep === index);
    });

    updateButtons();
  };

  const updateButtons = () => {
    prevButton.hidden = currentStep === 0;
    nextButton.hidden = currentStep >= steps.length - 1;
    submitButton.hidden = !nextButton.hidden;
  };

  const isValidStep = () => {
    const fields = steps[currentStep].querySelectorAll("input, textarea");
    return [...fields].every((field) => field.reportValidity());
  };

  //* event listeners

  const inputs = form.querySelectorAll("input, textarea");
  inputs.forEach((input) =>
    input.addEventListener("focus", (e) => {
      const focusedElement = e.target;

      // get the step where the focused element belongs
      const focusedStep = [...steps].findIndex((step) =>
        step.contains(focusedElement)
      );

      if (focusedStep !== -1 && focusedStep !== currentStep) {
        if (!isValidStep()) return;

        currentStep = focusedStep;
        updateProgress();
      }

      stepsContainer.scrollTop = 0;
      stepsContainer.scrollLeft = 0;
    })
  );

  form.addEventListener("submit", (e) => {
    e.preventDefault(); // prevent form submission

    if (!form.checkValidity()) return;

    const formData = new FormData(form);

    // send the data somewhere
    console.log(Object.fromEntries(formData));

    submitButton.disabled = true;
    submitButton.textContent = "Submitting...";

    // mimic a server request
    setTimeout(() => {
      form.querySelector(".completed").hidden = false;
    }, 3000);
  });

  prevButton.addEventListener("click", (e) => {
    e.preventDefault(); // prevent form submission

    if (currentStep > 0) {
      currentStep--;
      updateProgress();
    }
  });

  nextButton.addEventListener("click", (e) => {
    e.preventDefault(); // prevent form submission

    if (!isValidStep()) return;

    if (currentStep < steps.length - 1) {
      currentStep++;
      updateProgress();
    }
  });

  updateProgress();
});

 

Explanation

  1. HTML:
    • The form is divided into multiple div elements, each representing a step.
    • Each step contains form fields and navigation buttons (Next and Previous).
    • Only the first step is initially visible (class active).
  2. CSS:
    • Basic styling for the form and buttons.
    • Only the div with the class active is displayed, others are hidden (display: none).
  3. JavaScript:
    • Event listeners for the Next and Previous buttons change the active step.
    • The changeStep function hides the current step and shows the next or previous step based on the button clicked.

With this structure, you can easily add more steps or customize the form as needed.

CEO Piyush Gupta


Reviews

There are no reviews yet. Be the first one to write one.


0.0
Rated 0 out of 5
0 out of 5 stars (based on 0 reviews)
Excellent0%
Very good0%
Average0%
Poor0%
Terrible0%