/* Formulare: Progressive Enhancement — ohne JS normales POST + Redirect. Mit JS: Inline-Feldfehler (aria-describedby, Fokus aufs erste ungültige Feld), fetch-Submit, Status in aria-live-Region, Button-Sperre während des Sendens. */ (function () { 'use strict'; var form = document.querySelector('.form'); if (!form || !window.fetch) { return; } var status = form.querySelector('[data-form-status]'); var button = form.querySelector('button[type="submit"]'); var MESSAGES = { success: 'Danke für deine Anfrage! Wir melden uns so schnell wie möglich bei dir.', validation: 'Bitte prüfe deine Eingaben – die markierten Felder brauchen noch etwas Aufmerksamkeit.', ratelimit: 'Zu viele Anfragen in kurzer Zeit. Bitte versuche es in ein paar Minuten erneut.', busy: 'Bei uns gehen gerade ungewöhnlich viele Anfragen ein. Bitte versuche es später noch einmal.', mail: 'Deine Nachricht konnte gerade nicht versendet werden. Bitte versuche es später erneut.' }; function errorEl(field) { return field.id ? document.getElementById(field.id + '-error') : null; } function showFieldError(field) { var msg = errorEl(field); field.setAttribute('aria-invalid', 'true'); if (msg) { msg.textContent = field.validationMessage; msg.hidden = false; } } function clearFieldError(field) { var msg = errorEl(field); field.removeAttribute('aria-invalid'); if (msg) { msg.textContent = ''; msg.hidden = true; } } Array.prototype.forEach.call(form.elements, function (field) { field.addEventListener('input', function () { clearFieldError(field); }); field.addEventListener('change', function () { clearFieldError(field); }); }); /* Feldfehler der Server-Validierung übernehmen ({ "": "Meldung" }) — dieselben Elemente wie beim 422-Re-Render ohne JS. */ function showServerErrors(fields) { Object.keys(fields).forEach(function (id) { var field = document.getElementById(id); var msg = document.getElementById(id + '-error'); if (field) { field.setAttribute('aria-invalid', 'true'); } if (msg) { msg.textContent = fields[id]; msg.hidden = false; } }); } function clearAllFieldErrors() { Array.prototype.forEach.call(form.elements, clearFieldError); } function show(type, text) { var p = document.createElement('p'); p.className = type === 'success' ? 'form__success' : 'form__error'; p.textContent = text; status.replaceChildren(p); status.focus({ preventScroll: true }); status.scrollIntoView({ block: 'nearest', behavior: 'smooth' }); } form.addEventListener('submit', function (event) { if (!form.checkValidity()) { event.preventDefault(); var invalid = form.querySelectorAll(':invalid'); Array.prototype.forEach.call(invalid, showFieldError); if (invalid.length) { invalid[0].focus(); } return; } event.preventDefault(); var buttonLabel = button.textContent; button.disabled = true; button.textContent = 'Wird gesendet …'; fetch(form.action, { method: 'POST', body: new FormData(form), headers: { Accept: 'application/json' } }) .then(function (response) { return response.json(); }) .then(function (data) { clearAllFieldErrors(); if (data.ok) { show('success', MESSAGES.success); form.reset(); } else { if (data.fields) { showServerErrors(data.fields); } show('error', MESSAGES[data.error] || MESSAGES.mail); } }) .catch(function () { show('error', MESSAGES.mail); }) .finally(function () { button.disabled = false; button.textContent = buttonLabel; }); }); })();