Copied SVG to clipboard
Something went wrong
Copied code to clipboard
Something went wrong

Default

User image

Default

Name

  • Osmo Discount
    -25%
Osmo Basics/

Live Form Validation (Basic)

Live Form Validation (Basic)

Documentation

Webflow

Code

Setup: External Scripts

External Scripts in Webflow

Make sure to always put the External Scripts before the Javascript step of the resource.

In this video you learn where to put these in your Webflow project? Or how to include a paid GSAP Club plugin in your project?

HTML

Copy

Step 1: Copy structure to Webflow

Copy structure to Webflow

In the video below we described how you can copy + paste the structure of this resource to your Webflow project.

Copy to Webflow

Webflow structure is not required for this resource.

Step 1: Add HTML

HTML

Copy
<div data-form-validate="" class="form-group w-form">
  <form id="wf-form-Default-Form" name="wf-form-Default-Form" data-name="Default Form" method="get" class="form" data-wf-page-id="6769821f0c3d54c63be676ce" data-wf-element-id="c6a904ac-9041-909d-442f-9a89534d58b5">
    <div data-validate="" class="form-field-group">
      <label for="name" class="form-label">Name <span class="form-required">*</span></label>
      <div class="form-field">
        <input class="form-input w-input" maxlength="256" name="name" data-name="Name" min="3" placeholder="Osmo" type="text" id="name" required="">
        <div class="form-field-icon is--success"><svg xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 24 24" fill="none"><path d="M11.25 14.25L9 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M15 10.5L11.25 14.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M3 12C3 16.9706 7.02943 21 12 21C16.9706 21 21 16.9706 21 12C21 7.02943 16.9706 3 12 3C7.02943 3 3 7.02943 3 12Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"></path></svg></div>
        <div class="form-field-icon is--error"><svg xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 24 24" fill="none"><path opacity="0.1" d="M12 3C16.971 3 21 7.029 21 12C21 16.971 16.971 21 12 21C7.029 21 3 16.971 3 12C3 7.029 7.029 3 12 3Z" fill="currentColor" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M12 3C16.971 3 21 7.029 21 12C21 16.971 16.971 21 12 21C7.029 21 3 16.971 3 12C3 7.029 7.029 3 12 3Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"></path><path d="M12 12.5V7.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M11.996 14.5C11.444 14.5 10.996 14.948 11 15.5C11 16.052 11.448 16.5 12 16.5C12.552 16.5 13 16.052 13 15.5C13 14.948 12.552 14.5 11.996 14.5Z" fill="currentColor"></path></svg></div>
      </div>
    </div>
    <div data-validate="" class="form-field-group">
      <label for="email" class="form-label">Email Address <span class="form-required">*</span></label>
      <div class="form-field">
        <input class="form-input w-input" maxlength="256" name="email" data-name="Email" placeholder="hello@osmo.supply" type="email" id="email" required="">
        <div class="form-field-icon is--success"><svg xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 24 24" fill="none"><path d="M11.25 14.25L9 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M15 10.5L11.25 14.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M3 12C3 16.9706 7.02943 21 12 21C16.9706 21 21 16.9706 21 12C21 7.02943 16.9706 3 12 3C7.02943 3 3 7.02943 3 12Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"></path></svg></div>
        <div class="form-field-icon is--error"><svg xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 24 24" fill="none"><path opacity="0.1" d="M12 3C16.971 3 21 7.029 21 12C21 16.971 16.971 21 12 21C7.029 21 3 16.971 3 12C3 7.029 7.029 3 12 3Z" fill="currentColor" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M12 3C16.971 3 21 7.029 21 12C21 16.971 16.971 21 12 21C7.029 21 3 16.971 3 12C3 7.029 7.029 3 12 3Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"></path><path d="M12 12.5V7.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M11.996 14.5C11.444 14.5 10.996 14.948 11 15.5C11 16.052 11.448 16.5 12 16.5C12.552 16.5 13 16.052 13 15.5C13 14.948 12.552 14.5 11.996 14.5Z" fill="currentColor"></path></svg></div>
      </div>
    </div>
    <div for="message" data-validate="" class="form-field-group">
      <label for="message" class="form-label">Message <span class="form-required">*</span></label>
      <div class="form-field">
        <textarea class="form-input is--textarea w-input" maxlength="5000" name="message" data-name="Message" min="3" placeholder="Hello Osmo, " id="message" required=""></textarea>
        <div class="form-field-icon is--success"><svg xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 24 24" fill="none"><path d="M11.25 14.25L9 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M15 10.5L11.25 14.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M3 12C3 16.9706 7.02943 21 12 21C16.9706 21 21 16.9706 21 12C21 7.02943 16.9706 3 12 3C7.02943 3 3 7.02943 3 12Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"></path></svg></div>
        <div class="form-field-icon is--error"><svg xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 24 24" fill="none"><path opacity="0.1" d="M12 3C16.971 3 21 7.029 21 12C21 16.971 16.971 21 12 21C7.029 21 3 16.971 3 12C3 7.029 7.029 3 12 3Z" fill="currentColor" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M12 3C16.971 3 21 7.029 21 12C21 16.971 16.971 21 12 21C7.029 21 3 16.971 3 12C3 7.029 7.029 3 12 3Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"></path><path d="M12 12.5V7.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M11.996 14.5C11.444 14.5 10.996 14.948 11 15.5C11 16.052 11.448 16.5 12 16.5C12.552 16.5 13 16.052 13 15.5C13 14.948 12.552 14.5 11.996 14.5Z" fill="currentColor"></path></svg></div>
      </div>
    </div>
    <div class="form-field-group">
      <div class="form-divider"></div>
    </div>
    <div class="form-field-group">
      <div class="form-field">
        <div data-submit="" tabindex="0" class="form-submit-btn">
          <p class="form-submit-btn-p">Submit</p><input type="submit" data-wait="Please wait..." class="form-submit w-button" value="Submit">
        </div>
      </div>
    </div>
  </form>
  <div class="form-notifcation w-form-done">
    <div class="form-notification-bg"></div>
    <div class="form-notification-p">Success! We’ll be in touch soon.</div>
    <div class="form-notification-icon"><svg xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 24 24" fill="none"><path d="M11.25 14.25L9 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M15 10.5L11.25 14.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M3 12C3 16.9706 7.02943 21 12 21C16.9706 21 21 16.9706 21 12C21 7.02943 16.9706 3 12 3C7.02943 3 3 7.02943 3 12Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"></path></svg></div>
  </div>
  <div class="form-notifcation is--error w-form-fail">
    <div class="form-notification-bg"></div>
    <div class="form-notification-p">Something went wrong while submitting.</div>
    <div class="form-notification-icon"><svg xmlns="http://www.w3.org/2000/svg" width="100%" viewBox="0 0 24 24" fill="none"><path opacity="0.1" d="M12 3C16.971 3 21 7.029 21 12C21 16.971 16.971 21 12 21C7.029 21 3 16.971 3 12C3 7.029 7.029 3 12 3Z" fill="currentColor" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M12 3C16.971 3 21 7.029 21 12C21 16.971 16.971 21 12 21C7.029 21 3 16.971 3 12C3 7.029 7.029 3 12 3Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"></path><path d="M12 12.5V7.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M11.996 14.5C11.444 14.5 10.996 14.948 11 15.5C11 16.052 11.448 16.5 12 16.5C12.552 16.5 13 16.052 13 15.5C13 14.948 12.552 14.5 11.996 14.5Z" fill="currentColor"></path></svg></div>
  </div>
</div>

HTML structure is not required for this resource.

Step 2: Add CSS

CSS

Copy
.form-group {
  grid-column-gap: 1.5em;
  grid-row-gap: 1.5em;
  flex-flow: column;
  width: 25em;
  display: flex; 
}

.form {
  grid-column-gap: 1.5em;
  grid-row-gap: 1.5em;
  flex-flow: column;
  width: 100%;
  display: flex;
}

.form-field-group {
  grid-column-gap: .75em;
  grid-row-gap: .75em;
  flex-flow: column;
  align-items: flex-start;
  display: flex;
}

.form-field {
  width: 100%;
  position: relative;
}

.form-input {
  outline-offset: 0px;
  -webkit-appearance: none;
  appearance: none;
  box-sizing: border-box;
  vertical-align: middle;
  background-color: #efeeec;
  border: 1px solid #efeeec;
  border-radius: .328125em;
  outline: 0 #0000;
  height: auto;
  margin-bottom: 0;
  padding: .9em 3.5em .9em 1em;
  font-size: 1.125em;
  font-weight: 500;
  line-height: 1.2;
  box-shadow: 0 0 #0000;
}

.form-input:focus {
  border-color: #cbc8c5;
}

.form-input::placeholder {
  color: #1313134d;
  background-color: #efeeec;
}

.form-input.is--textarea {
  resize: vertical;
  min-height: 9em;
}

.form-submit-btn-p {
  margin-bottom: 0;
  font-size: 1.125em;
  font-weight: 500;
  line-height: 1.2;
}

.form-notification-p {
  color: inherit;
  font-size: 1.125em;
  font-weight: 500;
}

.form-notification-icon {
  pointer-events: none;
  color: inherit;
  justify-content: center;
  align-items: center;
  width: 3.5em;
  padding-left: 1em;
  padding-right: 1em;
  display: flex;
  position: absolute;
  top: 50%;
  right: 0;
  transform: translateY(-50%);
}

/* Field: Error */
[data-validate].is--error input,
[data-validate].is--error textarea {
  border-color: #FF4C24;
}

[data-validate].is--error .form-field-icon.is--error {
  opacity: 1;
}

/* Field: Success */
[data-validate].is--success .form-field-icon.is--success {
  opacity: 1;
}

@media screen and (max-width: 767px) {
  .form-group {
    width: 100%;
  }
}

Step 2: Add custom Javascript

Custom Javascript in Webflow

In this video, Ilja gives you some guidance about using JavaScript in Webflow:

Step 2: Add Javascript

Step 3: Add Javascript

Javascript

Copy
function initBasicFormValidation() {
  const forms = document.querySelectorAll('[data-form-validate]');

  forms.forEach((form) => {
    const fields = form.querySelectorAll('[data-validate] input, [data-validate] textarea');
    const submitButtonDiv = form.querySelector('[data-submit]'); // The div wrapping the submit button
    const submitInput = submitButtonDiv.querySelector('input[type="submit"]'); // The actual submit button

    // Capture the form load time
    const formLoadTime = new Date().getTime(); // Timestamp when the form was loaded

    // Function to validate individual fields (input or textarea)
    const validateField = (field) => {
      const parent = field.closest('[data-validate]'); // Get the parent div
      const minLength = field.getAttribute('min');
      const maxLength = field.getAttribute('max');
      const type = field.getAttribute('type');
      let isValid = true;

      // Check if the field has content
      if (field.value.trim() !== '') {
        parent.classList.add('is--filled');
      } else {
        parent.classList.remove('is--filled');
      }

      // Validation logic for min and max length
      if (minLength && field.value.length < minLength) {
        isValid = false;
      }

      if (maxLength && field.value.length > maxLength) {
        isValid = false;
      }

      // Validation logic for email input type
      if (type === 'email' && !/\S+@\S+\.\S+/.test(field.value)) {
        isValid = false;
      }

      // Add or remove success/error classes on the parent div
      if (isValid) {
        parent.classList.remove('is--error');
        parent.classList.add('is--success');
      } else {
        parent.classList.remove('is--success');
        parent.classList.add('is--error');
      }

      return isValid;
    };

    // Function to start live validation for a field
    const startLiveValidation = (field) => {
      field.addEventListener('input', function () {
        validateField(field);
      });
    };

    // Function to validate and start live validation for all fields, focusing on the first field with an error
    const validateAndStartLiveValidationForAll = () => {
      let allValid = true;
      let firstInvalidField = null;

      fields.forEach((field) => {
        const valid = validateField(field);
        if (!valid && !firstInvalidField) {
          firstInvalidField = field; // Track the first invalid field
        }
        if (!valid) {
          allValid = false;
        }
        startLiveValidation(field); // Start live validation for all fields
      });

      // If there is an invalid field, focus on the first one
      if (firstInvalidField) {
        firstInvalidField.focus();
      }

      return allValid;
    };

    // Anti-spam: Check if form was filled too quickly
    const isSpam = () => {
      const currentTime = new Date().getTime();
      const timeDifference = (currentTime - formLoadTime) / 1000; // Convert milliseconds to seconds
      return timeDifference < 5; // Return true if form is filled within 5 seconds
    };

    // Handle clicking the custom submit button
    submitButtonDiv.addEventListener('click', function () {
      // Validate the form first
      if (validateAndStartLiveValidationForAll()) {
        // Only check for spam after all fields are valid
        if (isSpam()) {
          alert('Form submitted too quickly. Please try again.');
          return; // Stop form submission
        }
        submitInput.click(); // Simulate a click on the <input type="submit">
      }
    });

    // Handle pressing the "Enter" key
    form.addEventListener('keydown', function (event) {
      if (event.key === 'Enter' && event.target.tagName !== 'TEXTAREA') {
        event.preventDefault(); // Prevent the default form submission

        // Validate the form first
        if (validateAndStartLiveValidationForAll()) {
          // Only check for spam after all fields are valid
          if (isSpam()) {
            alert('Form submitted too quickly. Please try again.');
            return; // Stop form submission
          }
          submitInput.click(); // Trigger our custom form submission
        }
      }
    });
  });
}

// Initialize Basic Form Validation
document.addEventListener('DOMContentLoaded', () => {
  initBasicFormValidation();
});

Step 3: Add custom CSS

Step 2: Add custom CSS

Custom CSS in Webflow

Curious about where to put custom CSS in Webflow? Ilja explains it in the below video:

CSS

Copy
/* Field: Error */
[data-validate].is--error input,
[data-validate].is--error textarea {
  border-color: #FF4C24;
}

[data-validate].is--error .form-field-icon.is--error {
  opacity: 1;
}

/* Field: Success */
[data-validate].is--success .form-field-icon.is--success {
  opacity: 1;
}

Implementation

Form Parent

Add the attribute [data-form-validate] to the outer container of your form. This is usually the parent element of the <form> element. This attribute signals the validation script where to look for form fields that need validation.

Form Element

Ensure the actual <form> element resides inside the container with [data-form-validate]. This is required for detecting form-level events such as “Enter” key presses.

Field Group

Add [data-validate] to each field group. A field group typically includes a <label> and its corresponding <input> or <textarea> element. Each group must have the [data-validate] attribute to enable validation.

Field Validation Rules

Input min and max

Use the attributes [min="3"] and/or [max="12"] with [data-validate] to define the minimum and maximum character limits for a field. These limits ensure that users enter values of appropriate lengths.

Email

No additional attributes are required for email validation. Email fields are automatically validated to ensure the entered address is in the correct format.

Additional Features

Validation Classes

The following classes are dynamically added or removed based on the validation state of a field:

  • Class .is--error — Applied when the field fails validation.
  • Class .is--success — Applied when the field passes validation.
  • Class .is--filled — Applied when the user has entered a value into the field.

Anti-Spam Protection

To minimize spam submissions. The form includes a built-in delay mechanism. If the form is submitted in less than 5 seconds after loading, the submission will be rejected with a notification. This feature helps block bots that attempt to submit forms instantly.

Custom Submit Button

For enhanced customization:

  • Wrap your submit button inside an element with the [data-submit] attribute.
  • The actual submit button should be an <input type="submit"> inside the [data-submit] container.
  • The script intercepts the click event on [data-submit], validates the form, and only submits it programmatically if all fields pass validation.

Keyboard Behavior

Pressing “Enter” in any input field will trigger the form’s custom submission logic, ensuring all validations are performed before submission. This behavior is disabled for <textarea> elements to allow users to add multi-line text.

Advanced Version

Need more functionality? We also have a Live Form Validation (Advanced)

Resource Details

Form
Live
Validate
Input
Textarea
Error
Notification

Original source

Dennis Snellenberg

Creator Credits

We always strive to credit creators as accurately as possible. While similar concepts might appear online, we aim to provide proper and respectful attribution.