Last active
October 21, 2019 19:37
-
-
Save abovedave/59e170f97bd936349bdee47483d81fe2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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