Skip to content

Instantly share code, notes, and snippets.

@abovedave
Last active October 21, 2019 19:37
Show Gist options
  • Save abovedave/59e170f97bd936349bdee47483d81fe2 to your computer and use it in GitHub Desktop.
Save abovedave/59e170f97bd936349bdee47483d81fe2 to your computer and use it in GitHub Desktop.
/*
Notes:
- Add class 'js-form-validate' to form you want to use and 'js-validate' to each field.
- Disables buttons with class 'btn' until form is valid.
- Uses HTML 5 'required' and 'pattern' attributes. 'title' provides the validation description to the user.
- Script adds a <span class="js-hint"></span> to the output after each <input> element for the error message.
- You can see more helper classes in the code below. e.g., 'has-error'.
Examples:
Min length
<div class="field">
<input class="js-validate" type="password" name="password" required pattern=".{6,}" title="Should be at least 6 characters long">
</div>
Checkbox
<label class="field">
<input type="checkbox" name="confirm" class="js-validate" title="Please confirm you understand the implications" required autocomplete="off">
I confirm something
</label>
Same as another field
<div class="field">
<input class="js-validate" type="password" name="password" required pattern=".{6,}" autofocus title="Should be at least 6 characters long">
</div>
<div class="field">
<input class="js-validate" type="password" name="password_confirm" required title="Should match the above password" data-same-as="newpassword">
</div>
Disable one field until another is filled
<div class="field">
<select name="twitter" class="js-validate" data-child="communityhandle">
<option value="" disabled="" selected="">What is your social platform?</option>
<option>Twitter</option>
<option>Instagram</option>
</select>
</div>
<div class="field">
<input placeholder="What is your handle?" id="communityhandle" name="handle" required disabled pattern=".{1,}" class="js-validate">
</div>
*/
// Get all the forms with the given class
var forms = document.querySelectorAll('.js-form-validate');
// Loop over each form
for (x = 0; x < forms.length; ++x) {
// This form instance
var form = forms[x]
// Find submit button
var button = forms[x].querySelectorAll('.btn')[0];
// Disable browser validation
form.setAttribute('novalidate', 'novalidate');
// Disable submit button
button.disabled = true;
// Disable default events
var allowSubmit = false;
// Inputs to validate
var inputs = form.querySelectorAll('.js-validate');
// Loop over each input, add a span for error messages
for (y = 0; y < inputs.length; ++y) {
var el = document.createElement('span');
el.setAttribute('aria-live', 'polite');
el.classList.add('js-hint', 'js-hide');
inputs[y].parentNode.appendChild(el);
}
// When something happens
var inputUpdated = function (event) {
for (y = 0; y < inputs.length; ++y) {
// Add custom validation
fieldMessage(inputs[y], event);
// Enable sumbit button if all fields valid
if (inputs[y].checkValidity()) {
form.classList.add('js-valid');
form.classList.remove('js-invalid');
button.disabled = false;
} else {
form.classList.add('js-invalid');
form.classList.remove('js-valid');
button.disabled = true;
break;
}
}
}
inputUpdated();
// On field events, validate fields
form.addEventListener('input', inputUpdated);
form.addEventListener('change', inputUpdated);
}
// Handle the field message to the user
function fieldMessage (field, event) {
// Error message
var msg = field.getAttribute('title') || 'This field is not valid.';
// Checkbox validation
if (field.getAttribute('type') === 'checkbox') {
if (!field.checked) {
return field.setCustomValidity('checkbox error');
} else {
return field.setCustomValidity(''); // :valid
}
}
// Find the error span added previously
var err = field.parentNode.querySelectorAll('.js-hint')[0];
// Instances where we need to compare two fields
var sameAs = document.getElementById(field.getAttribute('data-same-as'))
var isValid = sameAs ? (field.value === sameAs.value) : field.checkValidity();
if (sameAs && !isValid) {
field.setCustomValidity(msg);
} else {
field.setCustomValidity(''); // :valid
}
// Enble any fields which are dependant on this being filled
var child = document.getElementById(field.getAttribute('data-child'))
if (child && field.value) {
child.disabled = false;
} else if (child) {
child.value = '';
child.disabled = true;
}
// Append error message
if (
field.value.length > 0 &&
!isValid
) {
field.parentNode.classList.add('has-error');
err.innerHTML = msg;
err.classList.remove('js-hide');
}
// Hide error stuff if valid
if (isValid) {
field.parentNode.classList.remove('has-error');
err.classList.add('js-hide');
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment