Day 09: Form Validation States
Build a small contact form with basic client side validation.
JavaScript focus
- submit event
- prevent default
- checking empty fields
- showing inline errors
- success state
- bypass constraint validation
Nice extras
- focus first invalid field
- clear errors on input
- clear form w/ reset button
MDN prep
Contact Form
This is a working contact form. I respond to most messages!
The HTML
<section class="section">
<h2 class="secondary_heading">Form Validation</h2>
<form class="form" id="contact-form" method="post" action="https://formspree.io/f/xreovvbo" target="_blank" novalidate>
<!-- capture the page url -->
<input type="hidden" name="page_path" value="">
<input type="hidden" name="page_url" value="">
<!-- actual fields -->
<div class="form_field">
<label for="name">Name</label>
<input type="text" id="name" name="name">
<p class="error_message" id="name-error" hidden>Please enter your name.</p>
</div>
<div class="form_field">
<label for="email">Email</label>
<input type="email" id="email" name="email">
<p class="error_message" id="email-error" hidden>Please enter a valid email.</p>
</div>
<div class="form_field">
<label for="message-field">Message</label>
<textarea id="message-field" name="message-field" rows="5"></textarea>
<p class="error_message" id="message-error" hidden>Please enter a message.</p>
</div>
<!-- Bot/Honey trap (keep) -->
<!-- aria-hidden=true is valid on a div -->
<div aria-hidden="true" class="sr-only">
<input type="text" name="b_f792c8f7b713ce406dfcc49ad_e075a780fe" tabindex="-1" autocomplete="off" value="" />
</div>
<div class="form_buttons">
<!-- submit the form -->
<button type="submit" class="button">Send Message</button>
<!-- reset the form -->
<button type="reset" class="text_button">Reset</button>
</div>
<p class="success_message" id="form-success" hidden>Your form was submitted.</p>
</form>
</section>
The JavaScript
function initFormValidation() {
const root = document.querySelector(".form");
if (!root);
// get hidden field values
const path = root.querySelector("input[name=page_path]");
if (path) path.value = window.location.pathname;
const url = root.querySelector("input[name=page_url]");
if (url) url.value = window.location.href;
// fields the user fills out
// could probably loop through the inputs, too
const name = root.querySelector("input[name=name]");
const email = root.querySelector("input[name=email]");
const textarea = root.querySelector("textarea[name=message-field]");
// reset button
const reset = root.querySelector("button[type=reset]");
// inline errors/success
const nameError = root.querySelector("#name-error");
const emailError = root.querySelector("#email-error");
const messageError = root.querySelector("#message-error");
const formSuccess = root.querySelector("#form-success");
root.addEventListener("submit", (e) => {
if (name.value === null || name.value === "") {
e.preventDefault();
nameError.removeAttribute("hidden", "");
return;
} else {
nameError.setAttribute("hidden", "");
}
if (email.value === null || email.value === "") {
e.preventDefault();
emailError.removeAttribute("hidden", "");
return;
} else {
emailError.setAttribute("hidden", "");
}
if (textarea.value === null || textarea.value === "") {
e.preventDefault();
messageError.removeAttribute("hidden", "");
return;
} else {
messageError.setAttribute("hidden", "");
}
formSuccess.removeAttribute("hidden", "");
});
reset.addEventListener("click", () => {
if (!formSuccess.hasAttribute("hidden")) {
formSuccess.setAttribute("hidden", "");
}
});
}
initFormValidation();