Das Problem
Die Standard-HTML5-Validierung bietet visuelles Feedback (über :invalid oder :user-invalid), wird aber nicht automatisch mit Attributen für Bedienungshilfen wie aria-invalid synchronisiert.
Wenn Sie das Standard-:invalid-Styling verwenden, kündigen Screenreader möglicherweise „Ungültiger Eintrag“ an, sobald ein Nutzer ein erforderliches Feld auswählt, das derzeit leer ist. Das ist für Nutzer, die Hilfstechnologien verwenden, störend, da der Fehler angekündigt wird, bevor eine Interaktion stattgefunden hat.
Die Lösung
Der programmatische Zustand (aria-invalid="true") soll genau dann angewendet werden, wenn der visuelle Zustand (:user-invalid) angewendet wird. Da :user-invalid auf dem internen „user-interacted“-Flag des Browsers basiert, können wir mit JavaScript prüfen, ob dieser Selektor bei Standardinteraktionsereignissen übereinstimmt.
Weitere Informationen finden Sie unter MDN aria-invalid.
Implementierungsstrategie
- Visuelle Ebene: Verwenden Sie CSS-
:user-invalid, um Rahmen/Symbole anzuzeigen. - Bedienungshilfenebene: Verwenden Sie
aria-invalidundaria-errormessage, um den Status an Hilfstechnologien zu übermitteln. - Visuelle und Barrierefreiheitsebene verbinden: Erstellen Sie ein einfaches JavaScript-Dienstprogramm, das auf
blur- undinput-Ereignisse wartet, prüft, ob das Element mit:user-invalidübereinstimmt, und die ARIA-Attribute entsprechend aktualisiert.
Implementierungsleitfaden
1. HTML-Struktur
Verknüpfen Sie die Eingabe mit der Fehlermeldung, indem Sie aria-errormessage (oder aria-describedby für eine breitere Unterstützung) verwenden.
<form>
<div class="field">
<label for="email">Email</label>
<input
type="email"
id="email"
required
aria-errormessage="email-error"
>
<span id="email-error" class="error-msg">
Please enter a valid email address.
</span>
</div>
</form>
2. CSS
Steuern Sie die Sichtbarkeit der Fehlermeldung mit der nativen Pseudoklasse :user-invalid.
.error-msg {
display: none;
color: #d93025;
}
/* Show error message when input is user-invalid */
input:user-invalid ~ .error-msg {
display: block;
}
/* Optional: Visual cues on the input itself */
input:user-invalid {
border-color: #d93025;
}
3. JavaScript
Da es kein „UserInvalidChanged“-Ereignis gibt, müssen Sie Standardformularereignisse verwenden, um den Status zu prüfen.
const updateAriaState = (event) => {
const input = event.target;
if (!input.matches?.('input, textarea, select')) return;
// Check if the browser currently considers this input "user-invalid"
const isUserInvalid = input.matches(':user-invalid');
if (isUserInvalid) {
input.setAttribute('aria-invalid', 'true');
} else {
input.removeAttribute('aria-invalid');
}
};
// Listen on the document to handle dynamically added fields.
// 'blur' and 'focus' do not bubble, so we must use the capture phase (true).
document.addEventListener('blur', updateAriaState, true);
document.addEventListener('focus', updateAriaState, true);
// Also update on input if we've already shown the error,
// so the error clears immediately when fixed.
document.addEventListener('input', (event) => {
const input = event.target;
if (!input.matches?.('input, textarea, select')) return;
const hasAriaInvalid = input.hasAttribute('aria-invalid');
const ariaInvalid = input.getAttribute('aria-invalid');
if (hasAriaInvalid && ariaInvalid === 'true') {
updateAriaState(event);
}
});
Fallback und Browserunterstützung
Feature-Erkennung
Sie können die Unterstützung in CSS und JavaScript prüfen.
JavaScript-Prüfung:
if (!CSS.supports('selector(:user-invalid)')) {
// Fallback logic here
}
CSS für Fallback
Damit Ihre Fallback-Logik visuell nicht vom nativen Verhalten zu unterscheiden ist, müssen Sie Ihre Fehlerstile sowohl auf die Pseudoklasse als auch auf die Fallback-Klasse anwenden.
/* Apply error styles to both native selector and fallback class */
input:user-invalid,
input.user-invalid-fallback {
border-color: #d93025;
background-color: #fce8e6;
}
/* Show error message for both cases */
input:user-invalid ~ .error-msg,
input.user-invalid-fallback ~ .error-msg {
display: block;
}
Fallback-Logik
Wenn :user-invalid fehlt, erfassen Sie den Interaktionsstatus manuell mit einem WeakMap.
const UserInvalidFallback = (() => {
const dirtyState = new WeakMap();
const updateState = (input) => {
const isValid = input.checkValidity();
// Update both visual and ARIA state
input.classList.toggle('user-invalid-fallback', !isValid);
input.classList.toggle('user-valid-fallback', isValid);
if (!isValid) {
input.setAttribute('aria-invalid', 'true');
} else {
input.removeAttribute('aria-invalid');
}
};
const handleEvent = (event) => {
const input = event.target;
if (event.type === 'reset' && input.matches?.('form')) {
const controls = input.elements || [];
for (const control of controls) {
dirtyState.delete(control);
control.classList.remove('user-invalid-fallback');
control.classList.remove('user-valid-fallback');
control.removeAttribute('aria-invalid');
}
return;
}
if (!input.matches?.('input, textarea, select')) return;
if (event.type === 'input' || event.type === 'change') {
const state = dirtyState.get(input) || { hasInteracted: false, hasBlurred: false };
state.hasInteracted = true;
dirtyState.set(input, state);
if (state.hasBlurred) {
updateState(input);
}
} else if (event.type === 'blur') {
const state = dirtyState.get(input) || { hasInteracted: false, hasBlurred: false };
state.hasBlurred = true;
dirtyState.set(input, state);
if (state.hasInteracted) {
updateState(input);
}
}
};
const init = () => {
if (CSS.supports('selector(:user-invalid)')) return;
document.addEventListener('blur', handleEvent, true); // Capture phase required
document.addEventListener('input', handleEvent, true);
document.addEventListener('change', handleEvent, true);
document.addEventListener('reset', handleEvent, true); // Capture resets
};
return { init };
})();
// Initialize globally
UserInvalidFallback.init();
Weitere Überlegungen
aria-liveim Vergleich zuaria-errormessage:aria-errormessageverbindet die Eingabe mit dem Text, aber Screenreader kündigen sie möglicherweise nicht sofort an, wenn sie angezeigt wird (nur wenn der Fokus auf der Eingabe liegt).- Wenn Sie eine sofortige Ansage benötigen, wenn der Fehler auftritt (z. B. bei Unschärfe), können Sie
role="alert"oderaria-live="polite"zum Container für die Fehlermeldung hinzufügen. Testen Sie dies jedoch gründlich, um eine „doppelte Ansage“ zu vermeiden, wenn der Nutzer das Feld fokussiert, um den Fehler zu beheben.
Internationalisierung:
- Achten Sie darauf, dass der Textinhalt Ihrer Fehlermeldung (
#email-error) übersetzt ist. Die Logik bleibt gleich.
- Achten Sie darauf, dass der Textinhalt Ihrer Fehlermeldung (