<h1>Form tip: Focus fields with errors</h1>
<form action="#" method="POST">
<p>
<label for="name">What’s Your Name?</label>
<input id="name"
name="name"
required
aria-required="true"
data-error-required="Please enter your name"
>
</p>
<p>
<label for="email">What’s Your Email?</label>
<input type="email"
id="email"
name="email"
required
aria-required="true"
aria-describedby="description-email"
data-error-required="Please enter your email"
data-error-invalid="Your email doesn’t look right"
>
<small id="description-email">Your email address will only be used to respond to your message and will not be shared with anyone, ever.</small>
</p>
<p>
<label for="message">What’s the message?</label>
<textarea id="message"
name="message"
required
aria-required="true"
data-error-required="Please don’t forget to send a message"
></textarea>
</p>
<p><button type="submit">Send it in</button>
</form>
body {
margin: 1rem;
}
form {
max-width: 300px;
}
label {
display: block;
font-weight: bold
}
input, select, textarea {
display: block;
width: 100%;
margin: .35em 0;
}
input[type="radio"],
input[type="checkbox"] {
width: auto;
}
textarea {
min-height: 100px;
}
input ~ small,
textarea ~ small {
display: block;
font-style: italic;
}
input ~ strong,
textarea ~ strong {
display: block;
font-size: .9rem;
font-weight: normal;
color: red;
text-align: right;
margin: 0 0 .25rem;
}
View Compiled
var $validation_error = document.createElement('strong');
$validation_error.className = 'form-validation-error';
function resetValidation( $field, $parent ) {
$field.removeAttribute('aria-invalid');
$field.removeAttribute('aria-errormessage');
$parent.querySelectorAll('.' + $validation_error.className)
.forEach(function($el){
$parent.removeChild($el);
});
}
function isValid( $field, refocus ) {
refocus = refocus || false;
var validity = $field.validity,
$parent = $field.parentNode,
$message = false,
message = '',
message_id = $field.id + '-validation-error';
resetValidation( $field, $parent );
if ( validity.valid ) {
// valid
return true;
}
$message = $validation_error.cloneNode(true);
$message.id = message_id;
if ( $field.validity.valueMissing ) {
message = $field.dataset.errorRequired || 'You forgot to fill in this field';
}
else {
message = $field.dataset.errorInvalid || 'The value you submitted doesn’t look right';
}
$message.innerHTML = message;
if ( $field.nextElementSibling ) {
$parent.insertBefore($message, $field.nextElementSibling);
}
else {
$parent.appendChild($message);
}
$field.setAttribute('aria-errormessage', message_id);
$field.setAttribute('aria-invalid', 'true');
if (refocus) {
$field.focus();
}
// not valid
return false;
}
function validateMe( e ) {
var $form = e.target,
i = 0,
field_count = $form.elements.length,
$first_error = false;
for ( i; i< field_count; i++) {
var $field = $form.elements[i],
valid = isValid($field);
if ( !$first_error && !valid ) {
$first_error = $field;
}
}
if ( $first_error ){
e.preventDefault();
$first_error.focus();
}
}
var $forms = document.querySelectorAll('form');
$forms.forEach(function($form){
$form.setAttribute('novalidate','');
$form.addEventListener('submit', validateMe, false);
var field_count = $form.elements.length;
while (field_count--) {
$form.elements[field_count].addEventListener('change', function(e){
isValid(e.target, true);
}, false)
}
});
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.