<form id="loginForm" novalidate>
<div>
<label for="username">Username</label><br />
<input type="text" id="username" name="username" />
</div>
<div>
<label for="password">Password</label><br />
<input type="password" id="password" name="password" />
</div>
<button type="submit">Log In</button>
</form>
<script>
/**
* Map of validation error keys to their spoken messages.
* @type {{ [key: string]: string }}
*/
const errorMessages = {
usernameEmpty: "The Username field cannot be empty",
passwordEmpty: "The Password field cannot be empty",
// add more keys/messages here as needed...
};
/**
* Speaks a given message using the Web Speech API.
*
* @param {string} message - The text to speak.
* @param {string} [lang='en-US'] - The BCP-47 language code.
*/
function speakError(message, lang = "en-US") {
const utterance = new SpeechSynthesisUtterance(message);
utterance.lang = lang;
speechSynthesis.speak(utterance);
}
/**
* Validates that the given input is not empty, styles it, and speaks an error if it is.
*
* @param {HTMLInputElement} element - The input element to validate.
* @param {string} messageKey - The key identifying which error message to speak.
*/
function validateNotEmpty(element, messageKey) {
if (element.value.trim().length === 0) {
element.style.border = "1px solid red";
speakError(errorMessages[messageKey]);
} else {
element.style.border = "1px solid black";
}
}
/**
* @typedef {Object} FieldConfig
* @property {string} id - The DOM id of the input field.
* @property {string} messageKey - The key in errorMessages to use.
*/
/** @type {FieldConfig[]} */
const fields = [{
id: "username",
messageKey: "usernameEmpty"
},
{
id: "password",
messageKey: "passwordEmpty"
},
];
// Wire up blur listeners once DOM is ready
fields.forEach(({
id,
messageKey
}) => {
const el = /** @type {HTMLInputElement|null} */ (document.getElementById(id));
if (!el) return;
el.addEventListener("blur", () => validateNotEmpty(el, messageKey));
});
// Prevent form submission for demo purposes
const form = document.getElementById("loginForm");
if (form) {
form.addEventListener("submit", (e) => e.preventDefault());
}
</script>
form {
max-width: 400px;
margin: 2rem auto;
display: flex;
flex-direction: column;
gap: 1rem;
}
label {
font-weight: bold;
}
input {
padding: 0.5rem;
font-size: 1rem;
}
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.