feat(contact-form): enhance validation and accessibility
This commit is contained in:
parent
2f509982f2
commit
d742364f46
|
@ -54,31 +54,37 @@ pageScripts:
|
||||||
<legend>Contact Info</legend>
|
<legend>Contact Info</legend>
|
||||||
<div class="first-name">
|
<div class="first-name">
|
||||||
<label for="first-name">First Name:<span class="req-ask">*</span></label>
|
<label for="first-name">First Name:<span class="req-ask">*</span></label>
|
||||||
<input type="text" id="first-name" name="first-name" placeholder="Ex. Sally" required>
|
<input type="text" id="first-name" name="first-name" placeholder="Ex. Sally" required aria-describedby="first-name-error">
|
||||||
|
<span class="error-message" id="first-name-error" aria-live="polite"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="last-name">
|
<div class="last-name">
|
||||||
<label for="last-name">Last Name:<span class="req-ask">*</span></label>
|
<label for="last-name">Last Name:<span class="req-ask">*</span></label>
|
||||||
<input type="text" id="last-name" name="last-name" placeholder="Ex. Westfield" required>
|
<input type="text" id="last-name" name="last-name" placeholder="Ex. Westfield" required aria-describedby="last-name-error">
|
||||||
|
<span class="error-message" id="last-name-error" aria-live="polite"></span>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="organization">
|
<div class="organization">
|
||||||
<label for="organization">Organization:</label>
|
<label for="organization">Organization:</label>
|
||||||
<input type="text" id="organization" name="organization" placeholder="Optional">
|
<input type="text" id="organization" name="organization" placeholder="Optional" aria-describedby="organization-error">
|
||||||
|
<span class="error-message" id="organization-error" aria-live="polite"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="email">
|
<div class="email">
|
||||||
<label for="email">Email:<span class="req-ask">*</span></label>
|
<label for="email">Email:<span class="req-ask">*</span></label>
|
||||||
<input type="email" id="email" name="email" placeholder="Ex. user@domain.com" required>
|
<input type="email" id="email" name="email" placeholder="Ex. user@domain.com" required aria-describedby="email-error">
|
||||||
|
<span class="error-message" id="email-error" aria-live="polite"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="phone">
|
<div class="phone">
|
||||||
<label for="phone">Phone:<span class="req-ask">*</span></label>
|
<label for="phone">Phone:<span class="req-ask">*</span></label>
|
||||||
<input type="tel" id="phone" name="phone" placeholder="Ex. 111-333-4444" pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}" required>
|
<input type="tel" id="phone" name="phone" placeholder="Ex. 111-333-4444" pattern="[0-9]{3}-[0-9]{3}-[0-9]{4}" required aria-describedby="phone-error">
|
||||||
|
<span class="error-message" id="phone-error" aria-live="polite"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="preferred-method">
|
<div class="preferred-method" role="radiogroup" aria-labelledby="contact-method-label">
|
||||||
<h3>Preferred Contact Method</h3>
|
<h3 id="contact-method-label">Preferred Contact Method</h3>
|
||||||
<div class="radio-group">
|
<div class="radio-group">
|
||||||
<div class="email-radio">
|
<div class="email-radio">
|
||||||
<input type="radio" id="contact-email" name="contact-method" value="email" checked required>
|
<input type="radio" id="contact-email" name="contact-method" value="email" checked required>
|
||||||
|
@ -97,9 +103,13 @@ pageScripts:
|
||||||
<fieldset class="message-field">
|
<fieldset class="message-field">
|
||||||
<legend>Message</legend>
|
<legend>Message</legend>
|
||||||
<label for="message">Questions or Feedback</label>
|
<label for="message">Questions or Feedback</label>
|
||||||
<textarea id="message" name="message" rows="15" placeholder="What questions or feedback do you have for me?" required></textarea>
|
<textarea id="message" name="message" rows="15" placeholder="What questions or feedback do you have for me?" required aria-describedby="message-error"></textarea>
|
||||||
|
<span class="error-message" id="message-error" aria-live="polite"></span>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
<div class="submit-reset">
|
||||||
<button type="submit">Send Message</button>
|
<button type="submit">Send Message</button>
|
||||||
|
<button type="reset">Reset Form</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="form-note">* Required fields</p>
|
<p class="form-note">* Required fields</p>
|
||||||
|
|
|
@ -2,6 +2,7 @@ document.addEventListener('DOMContentLoaded', (event) => {
|
||||||
const accordionHeaders = document.querySelectorAll('.accordion-header');
|
const accordionHeaders = document.querySelectorAll('.accordion-header');
|
||||||
|
|
||||||
accordionHeaders.forEach(header => {
|
accordionHeaders.forEach(header => {
|
||||||
|
// Toggle content on click
|
||||||
header.addEventListener('click', () => {
|
header.addEventListener('click', () => {
|
||||||
const content = header.nextElementSibling;
|
const content = header.nextElementSibling;
|
||||||
if (content.style.display === 'block') {
|
if (content.style.display === 'block') {
|
||||||
|
@ -10,5 +11,13 @@ document.addEventListener('DOMContentLoaded', (event) => {
|
||||||
content.style.display = 'block';
|
content.style.display = 'block';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Toggle content on Enter or Space key press
|
||||||
|
header.addEventListener('keydown', (e) => {
|
||||||
|
if (e.key === 'Enter' || e.key === ' ') {
|
||||||
|
e.preventDefault();
|
||||||
|
header.click();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -1,68 +1,94 @@
|
||||||
// Contact Form Submission Script
|
// Contact Form Submission Script
|
||||||
|
|
||||||
// Select Form Element
|
|
||||||
const form = document.getElementById("contact-form");
|
const form = document.getElementById("contact-form");
|
||||||
|
const resetButton = form.querySelector('button[type="reset"]');
|
||||||
|
|
||||||
// Event Listener for Submit Event
|
|
||||||
form.addEventListener('submit', function(event) {
|
|
||||||
event.preventDefault(); // Prevent default form submission
|
|
||||||
|
|
||||||
// Variable Assignments
|
// Utility: Show error for specific input
|
||||||
const firstName = document.getElementById('first-name').value.trim();
|
function showError(inputId, message) {
|
||||||
const lastName = document.getElementById('last-name').value.trim();
|
const input = document.getElementById(inputId);
|
||||||
const organization = document.getElementById('organization').value.trim(); // Fixed typo
|
const errorSpan = document.getElementById(`${inputId}-error`);
|
||||||
const email = document.getElementById('email').value.trim();
|
if (input) input.classList.add("error");
|
||||||
const phone = document.getElementById('phone').value.trim();
|
if (errorSpan) errorSpan.textContent = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility: Clear all errors
|
||||||
|
function clearErrors() {
|
||||||
|
document.querySelectorAll(".error-message").forEach(span => span.textContent = "");
|
||||||
|
document.querySelectorAll("input, textarea").forEach(input => input.classList.remove("error"));
|
||||||
|
}
|
||||||
|
|
||||||
|
form.addEventListener("submit", function(event) {
|
||||||
|
event.preventDefault(); // Prevent actual form submission
|
||||||
|
|
||||||
|
clearErrors(); // Clear previous errors
|
||||||
|
|
||||||
|
// Get values
|
||||||
|
const firstName = document.getElementById("first-name").value.trim();
|
||||||
|
const lastName = document.getElementById("last-name").value.trim();
|
||||||
|
const organization = document.getElementById("organization").value.trim();
|
||||||
|
const email = document.getElementById("email").value.trim();
|
||||||
|
const phone = document.getElementById("phone").value.trim();
|
||||||
const contactMethod = document.querySelector('input[name="contact-method"]:checked')?.value;
|
const contactMethod = document.querySelector('input[name="contact-method"]:checked')?.value;
|
||||||
const message = document.getElementById('message').value.trim();
|
const message = document.getElementById("message").value.trim();
|
||||||
|
|
||||||
const errorMessages = [];
|
let hasErrors = false;
|
||||||
|
|
||||||
// First name validation
|
// First Name
|
||||||
if (!firstName) {
|
if (!firstName) {
|
||||||
errorMessages.push("Please enter your first name.");
|
showError("first-name", "Please enter your first name.");
|
||||||
|
hasErrors = true;
|
||||||
} else if (firstName.length < 2) {
|
} else if (firstName.length < 2) {
|
||||||
errorMessages.push("First name must be at least 2 characters.");
|
showError("first-name", "First name must be at least 2 characters.");
|
||||||
|
hasErrors = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Last name validation
|
// Last Name
|
||||||
if (!lastName) {
|
if (!lastName) {
|
||||||
errorMessages.push("Please enter your last name.");
|
showError("last-name", "Please enter your last name.");
|
||||||
|
hasErrors = true;
|
||||||
} else if (lastName.length < 2) {
|
} else if (lastName.length < 2) {
|
||||||
errorMessages.push("Last name must be at least 2 characters.");
|
showError("last-name", "Last name must be at least 2 characters.");
|
||||||
|
hasErrors = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Email validation
|
// Organization (optional)
|
||||||
|
if (organization.length > 0 && organization.length < 2) {
|
||||||
|
showError("organization", "Organization name must be at least 2 characters.");
|
||||||
|
hasErrors = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Email
|
||||||
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||||
if (!emailPattern.test(email)) {
|
if (!emailPattern.test(email)) {
|
||||||
errorMessages.push("Please enter a valid email address.");
|
showError("email", "Please enter a valid email address.");
|
||||||
|
hasErrors = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phone number validation (exactly 10 digits)
|
// Phone — match format: 123-456-7890
|
||||||
const phonePattern = /^\d{10}$/;
|
const phonePattern = /^\d{3}-\d{3}-\d{4}$/;
|
||||||
if (!phonePattern.test(phone)) {
|
if (!phonePattern.test(phone)) {
|
||||||
errorMessages.push("Phone number must be exactly 10 digits (numbers only).");
|
showError("phone", "Phone number must be in the format 123-456-7890.");
|
||||||
|
hasErrors = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Message validation
|
// Message
|
||||||
if (message.length < 10) {
|
if (message.length < 10) {
|
||||||
errorMessages.push("Message must be at least 10 characters long.");
|
showError("message", "Message must be at least 10 characters long.");
|
||||||
|
hasErrors = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optional: Organization validation (only if provided)
|
if (!hasErrors) {
|
||||||
if (organization.length > 0 && organization.length < 2) {
|
|
||||||
errorMessages.push("Organization name must be at least 2 characters if provided.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display errors or submit
|
|
||||||
const errorDiv = document.getElementById('errorMessages');
|
|
||||||
if (errorMessages.length > 0) {
|
|
||||||
errorDiv.innerHTML = errorMessages.join("<br>");
|
|
||||||
} else {
|
|
||||||
errorDiv.innerHTML = "";
|
|
||||||
alert("Form submitted successfully!");
|
alert("Form submitted successfully!");
|
||||||
// this.submit(); // Uncomment if actually submitting to a server
|
// form.submit(); // Uncomment when ready to send to server
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// This script isn't finished.
|
form.addEventListener("reset", function (event) {
|
||||||
|
const confirmed = confirm("Are you sure you want to clear the form?");
|
||||||
|
if (!confirmed) {
|
||||||
|
event.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
clearErrors();
|
||||||
|
});
|
||||||
|
|
|
@ -137,11 +137,26 @@ legend {
|
||||||
}
|
}
|
||||||
|
|
||||||
label {
|
label {
|
||||||
display: block;
|
font-weight: 600;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
color: #333;
|
||||||
margin-bottom: 0.4rem;
|
margin-bottom: 0.4rem;
|
||||||
|
display: block;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
color: red;
|
||||||
|
font-size: 0.9em;
|
||||||
|
margin-top: 4px;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.error, textarea.error {
|
||||||
|
border-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
input[type="text"],
|
input[type="text"],
|
||||||
input[type="email"],
|
input[type="email"],
|
||||||
input[type="tel"],
|
input[type="tel"],
|
||||||
|
@ -184,7 +199,13 @@ textarea:focus {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Button */
|
/* Button */
|
||||||
button[type="submit"] {
|
.submit-reset {
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
button[type="submit"], button[type="reset"] {
|
||||||
margin:20px 0px 20px 0px;
|
margin:20px 0px 20px 0px;
|
||||||
padding: 8px 25px;
|
padding: 8px 25px;
|
||||||
background-color: #ea7e0b;
|
background-color: #ea7e0b;
|
||||||
|
@ -196,7 +217,7 @@ button[type="submit"] {
|
||||||
transition: background-color 0.3s ease;
|
transition: background-color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
button[type="submit"]:hover {
|
button[type="submit"]:hover, button[type="reset"]:hover {
|
||||||
background-color: #2e97be;
|
background-color: #2e97be;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue