<p>Required fields are marked with an asterisk <abbr title="Required field">*</abbr></p>
<!-- We're using novalidate attribute here to avoid the built in browser validation -->
<form method="post" action="/" id="test-form" novalidate>
	<div id="form-message" class="form-message" role="alert" aria-atomic="true"></div>
	<div class="form-field">
		<label for="name">Name <abbr title="required">*</abbr></label>
		<input type="name" name="name" id="name" required />
	<div class="form-field">
		<label for="email">E-mail <abbr title="required">*</abbr></label>
		<input type="email" name="email" id="email" required />
	</div>
	<button type="submit">Send</button>
</form>
label {
	display: block;
}

.form-message {
	background-color: #ffffd4;
}

.form-field {
	margin: 1rem 0;
}

.field-error {
	color: maroon;
}
/* This javascript is only made to make a quick
 * demonstration of the form states when validating.
 * The code itself is a quick hack and nothing that 
 * should be used in the real world (wide web).
 * I'm not proud of this...
 */

const testForm = document.getElementById("test-form");
const formMessageHolder = document.getElementById("form-message");
const nameField = document.getElementById("name");
const emailField = document.getElementById("email");

testForm.addEventListener("submit", event => {
	event.preventDefault();
	const nameValue = nameField.value;
	const emailValue = emailField.value;
	const nameDescNode = document.getElementById("nameDesc");
	const emailDescNode = document.getElementById("emailDesc");

	// Cleanup inline error messages
	if (nameDescNode) {
		nameDescNode.parentNode.removeChild(nameDescNode);
	}
	if (emailDescNode) {
		emailDescNode.parentNode.removeChild(emailDescNode);
	}
	
	// Display error message if form is invalid
	if (nameValue === "" || emailValue === "") {
		formMessageHolder.innerHTML = `
			<p>Please review the form and fix the following errors:</p>
			<ul>
				${nameValue ? "" : "<li>The name field must be filled in.</li>"}
				${emailValue ? "" : "<li>The e-mail field must be filled in.</li>"}
			</ul>
		`;

		if (nameValue === "") {
			createErrorDescription("nameDesc", nameField, "The name field must be filled in.");
		}

		if (emailValue === "") {
			createErrorDescription("emailDesc", emailField, "The e-mail field must be filled in.");
		} 

		// Set focus on the first invalid field
		nameValue === "" ? nameField.focus() : emailField.focus();
	} else {
		// Display success message if form was sent successfully
		formMessageHolder.innerHTML = "<p>The form was sent successfully!</p>";
	}
});

// Helper method to create an field error message
const createErrorDescription = (name, field, message) => {
	var newNode = document.createElement("div");
	newNode.setAttribute("id", name);
	newNode.classList.add("field-error");
	field.setAttribute("aria-describedby", name);
	newNode.innerHTML = message;
	field.after(newNode);
};

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.