Live Form Validation (Basic)
Lightweight live form validation for Webflow covering text inputs, email, and textarea fields. Includes anti-spam protection and a custom submit handler. No support for select, radio, or checkbox groups — use the Advanced version for those.
validationjavascriptwebflowemail
Code
HTML
html
<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">
<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"><!-- success SVG --></div>
<div class="form-field-icon is--error"><!-- error 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"><!-- success SVG --></div>
<div class="form-field-icon is--error"><!-- error 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"><!-- success SVG --></div>
<div class="form-field-icon is--error"><!-- error 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"><!-- success 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"><!-- error SVG --></div>
</div>
</div>CSS
css
.form-group {
grid-column-gap: 1.5em;
grid-row-gap: 1.5em;
flex-flow: column;
width: 25em;
margin-bottom: 0;
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-label {
color: #131313;
width: 100%;
margin-bottom: 0;
font-size: .875em;
font-weight: 500;
line-height: 1;
}
.form-required {
color: #ff4c24;
}
.form-input {
outline-offset: 0px;
color: #131313;
-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.is--textarea {
resize: vertical;
min-height: 9em;
}
.form-input:focus {
border-color: #cbc8c5;
}
.form-input::placeholder {
color: #1313134d;
background-color: #efeeec;
}
.form-field-icon {
opacity: 0;
pointer-events: none;
color: #cbc8c5;
-webkit-user-select: none;
user-select: none;
border-radius: .375em;
justify-content: center;
align-items: center;
width: 3.5em;
max-height: 3.5em;
padding-left: 1em;
padding-right: 1em;
display: flex;
position: absolute;
top: 1px;
bottom: 1px;
right: 1px;
}
.form-field-icon.is--error {
color: #ff4c24;
}
.form-submit {
visibility: hidden;
opacity: 0;
position: absolute;
inset: 0;
}
.form-submit-btn {
outline-offset: 0px;
color: #efeeec;
cursor: pointer;
background-color: #131313;
border: 1px solid #131313;
border-radius: .375rem;
outline: 0 #0000;
flex-flow: row;
justify-content: flex-start;
align-items: center;
padding: 1.005em 1.125em;
display: flex;
position: relative;
overflow: hidden;
box-shadow: inset 0 0 #0000;
}
.form-submit-btn:focus {
outline-offset: 0px;
border-width: 1px;
border-color: #cbc8c5;
outline-color: #131313;
}
.form-submit-btn-p {
margin-bottom: 0;
font-size: 1.125em;
font-weight: 500;
line-height: 1.2;
}
.form-divider {
opacity: .15;
background-color: #131313;
width: 100%;
height: 1px;
}
.form-notifcation {
outline-offset: 0px;
color: #0ba954;
text-align: left;
border: 1px solid #0ba954;
border-color: inherit;
background-color: #efeeec;
border-radius: .375rem;
outline: 0 #0000;
width: 100%;
padding: 1.125em;
font-size: 1em;
position: relative;
}
.form-notifcation.is--error {
color: #ff4c24;
margin-top: 0;
padding-right: 3.5em;
}
.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%);
}
.form-notification-bg {
opacity: .1;
pointer-events: none;
color: inherit;
background-color: currentColor;
border-radius: calc(.375rem - 2px);
display: flex;
position: absolute;
inset: 0;
}
.form-notification-p {
color: inherit;
font-size: 1.125em;
font-weight: 500;
}
@media screen and (max-width: 767px) {
.form-group {
width: 100%;
}
}
/* 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;
}JavaScript
javascript
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]');
const submitInput = submitButtonDiv.querySelector('input[type="submit"]');
const formLoadTime = new Date().getTime();
const validateField = (field) => {
const parent = field.closest('[data-validate]');
const minLength = field.getAttribute('min');
const maxLength = field.getAttribute('max');
const type = field.getAttribute('type');
let isValid = true;
if (field.value.trim() !== '') {
parent.classList.add('is--filled');
} else {
parent.classList.remove('is--filled');
}
if (minLength && field.value.length < minLength) {
isValid = false;
}
if (maxLength && field.value.length > maxLength) {
isValid = false;
}
if (type === 'email' && !/\S+@\S+\.\S+/.test(field.value)) {
isValid = false;
}
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;
};
const startLiveValidation = (field) => {
field.addEventListener('input', function () {
validateField(field);
});
};
const validateAndStartLiveValidationForAll = () => {
let allValid = true;
let firstInvalidField = null;
fields.forEach((field) => {
const valid = validateField(field);
if (!valid && !firstInvalidField) {
firstInvalidField = field;
}
if (!valid) {
allValid = false;
}
startLiveValidation(field);
});
if (firstInvalidField) {
firstInvalidField.focus();
}
return allValid;
};
const isSpam = () => {
const currentTime = new Date().getTime();
const timeDifference = (currentTime - formLoadTime) / 1000;
return timeDifference < 5;
};
submitButtonDiv.addEventListener('click', function () {
if (validateAndStartLiveValidationForAll()) {
if (isSpam()) {
alert('Form submitted too quickly. Please try again.');
return;
}
submitInput.click();
}
});
form.addEventListener('keydown', function (event) {
if (event.key === 'Enter' && event.target.tagName !== 'TEXTAREA') {
event.preventDefault();
if (validateAndStartLiveValidationForAll()) {
if (isSpam()) {
alert('Form submitted too quickly. Please try again.');
return;
}
submitInput.click();
}
}
});
});
}
// Initialize Basic Form Validation
document.addEventListener('DOMContentLoaded', () => {
initBasicFormValidation();
});Attributes
| Name | Type | Default | Description |
|---|---|---|---|
| data-form-validate | attribute | — | Add to the outer container (parent of the <form> element). Signals the script where to find the form. |
| data-validate | attribute | — | Add to each field group (label + input/textarea). Required on every group that needs validation. |
| data-submit | attribute | — | Wrap the submit button. The script intercepts the click, validates all fields, then triggers the real submit only if valid. |
| min | number | — | Minimum character length required for the field to pass validation. |
| max | number | — | Maximum character length allowed for the field. |
Notes
- •Only supports <input> and <textarea> fields — no select, radio, or checkbox validation.
- •Live validation starts on each field only after the user first interacts (input event). On submit, all fields are validated at once.
- •Anti-spam: submission is blocked if the form is submitted in less than 5 seconds after page load.
- •Pressing Enter in any input (except <textarea>) triggers the full validation and custom submit logic.
- •Dynamic classes: .is--error (fails validation), .is--success (passes validation), .is--filled (field has a value).
- •Need select, radio, or checkbox support? Use Live Form Validation (Advanced) instead.