4.1.3 - Állapotüzenetek
Röviden a szabványpontról
A WCAG 2.2 Success Criterion 4.1.3 (Status Messages) megköveteli, hogy a weboldalakon megjelenő állapotüzenetek programozottan meghatározhatók legyenek, így a kisegítő technológiák (mint a képernyőolvasók) be tudják mutatni ezeket az üzeneteket a felhasználóknak anélkül, hogy elmozdítanák a billentyűzet fókuszt. Ez azt jelenti, hogy a kisegítő technológiákra támaszkodó felhasználók időben értesülnek a változásokról, vagy a rendszer visszajelzéseiről anélkül, hogy el kelljen navigáljanak és elveszítsék az információt, hogy meddig haladtak oldalon.
Cél: Biztosítani, hogy a képernyőolvasóra vagy más kisegítő technológiákra támaszkodó felhasználók értesüljenek a felület változásairól és állapotfrissítéseiről anélkül, hogy megszakítanánk jelenlegi interakciójukat vagy fókuszukat.
Mire vonatkozik: Dinamikus tartalomfrissítésekre, mint például űrlapvalidációs üzenetek, sikeres vagy hibás értesítések, és más élő állapotfrissítések, amelyek nem járnak fókuszváltással, de fontosak a felhasználó tájékoztatásához.
Kiket érint
Elsődleges felhasználók: Látássérült vagy vak emberek, akik képernyőolvasót használnak a tartalom navigálásához. Ők a programozottan elérhető állapotüzenetekre támaszkodnak, hogy értesüljenek az olyan változásokról, mint az űrlaphibák, beküldési eredmények vagy valós idejű frissítések.
Másodlagos előnyök: A kognitív fogyatékossággal élő felhasználók is profitálnak, mert a világos állapotüzenetek segítenek tisztázni a rendszer válaszait. Továbbá azok a felhasználók, akik több feladatot végeznek egyszerre vagy figyelemzavarral küzdenek, hasznot húznak a nem zavaró, hozzáférhető értesítésekből.
Tesztelés
- Képernyőolvasó tesztelés: Használj népszerű képernyőolvasókat (NVDA, JAWS, VoiceOver) annak megerősítésére, hogy az állapotüzenetek bejelentésre kerülnek megjelenésükkor anélkül, hogy elmozgatnák a billentyűzet fókuszt
- ARIA attribútumok vizsgálata: Ellenőrizd, hogy a dinamikus állapotüzenetek megfelelő ARIA szerepeket használnak, mint a role=”status” vagy aria-live=”polite”
- Billentyűzetes navigáció: Biztosítsd, hogy a billentyűzet fókusz stabil marad, amikor állapotüzenetek jelennek meg; az üzenetek ne lopják el a fókuszt
- Automatizált akadálymentességi tesztelő eszközök: Használj eszközöket, mint az axe DevTools a hiányzó vagy helytelen ARIA élő régiók észlelésére
- Kód vizsgálat: Tekintsd át a HTML és JavaScript kódot annak ellenőrzésére, hogy az állapotüzenetek élő régió szerepekkel rendelkező elemekben kerülnek beillesztésre vagy frissítésre
Jó gyakorlatok
1. Űrlap validációs visszajelzések
<!-- REGISZTRÁCIÓS ŰRLAP - Élő állapotüzenetekkel -->
<form id="regisztracio-form" class="user-form" novalidate>
<div class="form-header">
<h2>Új fiók létrehozása</h2>
<p>Töltse ki az alábbi mezőket a regisztrációhoz</p>
</div>
<!-- E-mail mező valós idejű validációval -->
<div class="form-group">
<label for="email">E-mail cím</label>
<input
type="email"
id="email"
name="email"
required
aria-describedby="email-help email-status"
autocomplete="email"
>
<span id="email-help" class="field-help">
Használjon érvényes e-mail formátumot (pl. nev@domain.hu)
</span>
<!-- Élő állapot régió a validációs üzenetekhez -->
<div
id="email-status"
role="status"
aria-live="polite"
aria-atomic="true"
class="status-message"
></div>
</div>
<!-- Jelszó mező erősség indikátorral -->
<div class="form-group">
<label for="jelszo">Jelszó</label>
<input
type="password"
id="jelszo"
name="jelszo"
required
minlength="8"
aria-describedby="jelszo-help jelszo-strength"
autocomplete="new-password"
>
<span id="jelszo-help" class="field-help">
Legalább 8 karakter, tartalmaz számot és nagybetűt
</span>
<!-- Jelszó erősség élő visszajelzés -->
<div
id="jelszo-strength"
role="status"
aria-live="polite"
class="password-strength"
></div>
</div>
<!-- Felhasználónév elérhetőség ellenőrzés -->
<div class="form-group">
<label for="felhasznalonev">Felhasználónév</label>
<input
type="text"
id="felhasznalonev"
name="felhasznalonev"
required
minlength="3"
maxlength="20"
aria-describedby="felhasznalonev-help felhasznalonev-status"
autocomplete="username"
>
<span id="felhasznalonev-help" class="field-help">
3-20 karakter, csak betűk és számok
</span>
<!-- Felhasználónév elérhetőség állapot -->
<div
id="felhasznalonev-status"
role="status"
aria-live="polite"
aria-busy="false"
class="availability-status"
></div>
</div>
<button type="submit" class="btn-primary">Regisztráció</button>
<!-- Általános űrlap állapot üzenetek -->
<div
id="form-status"
role="status"
aria-live="polite"
aria-atomic="true"
class="form-status-message"
></div>
</form>
<script>
const emailInput = document.getElementById('email');
const emailStatus = document.getElementById('email-status');
const jelszoInput = document.getElementById('jelszo');
const jelszoStrength = document.getElementById('jelszo-strength');
const felhasznalonevInput = document.getElementById('felhasznalonev');
const felhasznalonevStatus = document.getElementById('felhasznalonev-status');
const formStatus = document.getElementById('form-status');
// E-mail validáció
let emailTimeout;
emailInput.addEventListener('input', function() {
clearTimeout(emailTimeout);
// Kis késleltetés a gépelés befejezése után
emailTimeout = setTimeout(() => {
const email = this.value.trim();
if (email === '') {
emailStatus.textContent = '';
emailStatus.className = 'status-message';
} else if (!isValidEmail(email)) {
emailStatus.textContent = 'Érvénytelen e-mail formátum. Kérjük, ellenőrizze.';
emailStatus.className = 'status-message error';
} else {
emailStatus.textContent = 'Az e-mail cím formátuma megfelelő.';
emailStatus.className = 'status-message success';
}
}, 500);
});
// Jelszó erősség ellenőrzés
jelszoInput.addEventListener('input', function() {
const jelszo = this.value;
let erosseg = 0;
let uzenet = '';
if (jelszo.length === 0) {
jelszoStrength.textContent = '';
return;
}
// Erősség számítás
if (jelszo.length >= 8) erosseg++;
if (jelszo.length >= 12) erosseg++;
if (/[a-z]/.test(jelszo)) erosseg++;
if (/[A-Z]/.test(jelszo)) erosseg++;
if (/[0-9]/.test(jelszo)) erosseg++;
if (/[^a-zA-Z0-9]/.test(jelszo)) erosseg++;
// Üzenet és stílus beállítása
if (erosseg <= 2) {
uzenet = 'Gyenge jelszó. Ajánlott több karaktertípus használata.';
jelszoStrength.className = 'password-strength weak';
} else if (erosseg <= 4) {
uzenet = 'Közepes erősségű jelszó. Megfelelő a legtöbb célra.';
jelszoStrength.className = 'password-strength medium';
} else {
uzenet = 'Erős jelszó. Kiváló választás!';
jelszoStrength.className = 'password-strength strong';
}
jelszoStrength.textContent = uzenet;
});
// Felhasználónév elérhetőség ellenőrzés
let nevTimeout;
felhasznalonevInput.addEventListener('input', function() {
clearTimeout(nevTimeout);
const nev = this.value.trim();
if (nev.length < 3) {
felhasznalonevStatus.textContent = '';
return;
}
// Ellenőrzés indítása
felhasznalonevStatus.setAttribute('aria-busy', 'true');
felhasznalonevStatus.textContent = 'Felhasználónév ellenőrzése...';
felhasznalonevStatus.className = 'availability-status checking';
// Szimulált szerver ellenőrzés
nevTimeout = setTimeout(() => {
felhasznalonevStatus.setAttribute('aria-busy', 'false');
// Véletlenszerű eredmény szimulálása
const foglalt = Math.random() > 0.7;
if (foglalt) {
felhasznalonevStatus.textContent = `A "${nev}" felhasználónév már foglalt. Próbáljon másikat.`;
felhasznalonevStatus.className = 'availability-status unavailable';
} else {
felhasznalonevStatus.textContent = `A "${nev}" felhasználónév elérhető.`;
felhasznalonevStatus.className = 'availability-status available';
}
}, 1000);
});
// Űrlap beküldés
document.getElementById('regisztracio-form').addEventListener('submit', function(e) {
e.preventDefault();
// Küldés állapot
formStatus.textContent = 'Regisztráció feldolgozása...';
formStatus.className = 'form-status-message processing';
// Szimulált feldolgozás
setTimeout(() => {
formStatus.textContent = 'Sikeres regisztráció! Aktiváló e-mail elküldve.';
formStatus.className = 'form-status-message success';
// Űrlap visszaállítása
this.reset();
// Mezők állapotának törlése
emailStatus.textContent = '';
jelszoStrength.textContent = '';
felhasznalonevStatus.textContent = '';
}, 2000);
});
function isValidEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
</script>
<style>
.status-message {
margin-top: 4px;
font-size: 14px;
padding: 4px 8px;
border-radius: 4px;
min-height: 24px;
}
.status-message.error {
background-color: #fee;
color: #c00;
border-left: 3px solid #c00;
}
.status-message.success {
background-color: #efe;
color: #060;
border-left: 3px solid #060;
}
.password-strength {
margin-top: 4px;
padding: 4px 8px;
border-radius: 4px;
font-size: 14px;
}
.password-strength.weak {
background-color: #fee;
color: #c00;
}
.password-strength.medium {
background-color: #ffc;
color: #660;
}
.password-strength.strong {
background-color: #efe;
color: #060;
}
.availability-status {
margin-top: 4px;
font-size: 14px;
padding: 4px 8px;
border-radius: 4px;
}
.availability-status.checking {
background-color: #eef;
color: #006;
}
.availability-status.available {
background-color: #efe;
color: #060;
}
.availability-status.unavailable {
background-color: #fee;
color: #c00;
}
.form-status-message {
margin-top: 16px;
padding: 12px;
border-radius: 4px;
font-weight: bold;
}
.form-status-message.processing {
background-color: #eef;
color: #006;
}
.form-status-message.success {
background-color: #efe;
color: #060;
}
</style>
2. Valós idejű frissítések és értesítések
<!-- ÉLŐBEN FRISSÜLŐ ALKALMAZÁS -->
<div class="live-app">
<h2>Élő értesítések demonstráció</h2>
<!-- Bevásárlókosár frissítések -->
<div class="shopping-section">
<h3>Bevásárlókosár</h3>
<div class="cart-items">
<p>Jelenlegi kosár: <span id="cart-count">0</span> termék</p>
</div>
<button type="button" id="add-to-cart" class="btn-primary">
Termék hozzáadása
</button>
<!-- Kosár állapot üzenetek -->
<div
id="cart-status"
role="status"
aria-live="polite"
aria-relevant="additions text"
class="cart-notifications"
></div>
</div>
<!-- Chat alkalmazás példa -->
<div class="chat-section">
<h3>Csevegés</h3>
<div class="chat-window" aria-label="Csevegés ablak">
<div id="chat-messages" class="messages">
<p>Üdvözöljük a csevegésben!</p>
</div>
</div>
<!-- Új üzenetek értesítése -->
<div
id="chat-updates"
role="status"
aria-live="polite"
aria-relevant="additions"
class="sr-only"
></div>
<button type="button" id="simulate-message" class="btn-secondary">
Új üzenet szimulálása
</button>
</div>
<!-- Letöltési folyamat -->
<div class="download-section">
<h3>Fájl letöltés</h3>
<button type="button" id="start-download" class="btn-primary">
Letöltés indítása
</button>
<!-- Letöltési állapot -->
<div
id="download-progress"
role="status"
aria-live="polite"
aria-relevant="all"
class="download-status"
></div>
</div>
<!-- Automatikus mentés értesítés -->
<div class="autosave-section">
<h3>Szövegszerkesztő</h3>
<textarea
id="editor"
rows="5"
cols="50"
aria-describedby="autosave-status"
placeholder="Kezdjen el gépelni..."
></textarea>
<!-- Automatikus mentés állapot -->
<div
id="autosave-status"
role="status"
aria-live="polite"
aria-atomic="true"
class="autosave-indicator"
></div>
</div>
</div>
<script>
// Bevásárlókosár példa
let cartCount = 0;
const cartStatus = document.getElementById('cart-status');
const cartCountDisplay = document.getElementById('cart-count');
document.getElementById('add-to-cart').addEventListener('click', function() {
cartCount++;
cartCountDisplay.textContent = cartCount;
// Állapotüzenet frissítése
cartStatus.textContent = `Termék hozzáadva a kosárhoz. Összesen ${cartCount} termék van a kosárban.`;
// Üzenet eltűntetése 3 másodperc után
setTimeout(() => {
cartStatus.textContent = '';
}, 3000);
});
// Chat példa
const chatMessages = document.getElementById('chat-messages');
const chatUpdates = document.getElementById('chat-updates');
let messageCount = 0;
document.getElementById('simulate-message').addEventListener('click', function() {
messageCount++;
const senderek = ['Anna', 'Béla', 'Csilla', 'Dénes'];
const sender = senderek[Math.floor(Math.random() * senderek.length)];
// Új üzenet hozzáadása
const newMessage = document.createElement('p');
newMessage.textContent = `${sender}: Szia! Ez az ${messageCount}. üzenet.`;
chatMessages.appendChild(newMessage);
// Képernyőolvasó értesítése
chatUpdates.textContent = `Új üzenet ${sender}-tól/től`;
// Scroll a legújabb üzenethez
chatMessages.scrollTop = chatMessages.scrollHeight;
});
// Letöltés példa
const downloadProgress = document.getElementById('download-progress');
document.getElementById('start-download').addEventListener('click', function() {
this.disabled = true;
let progress = 0;
// Kezdeti állapot
downloadProgress.textContent = 'Letöltés megkezdve...';
downloadProgress.className = 'download-status downloading';
// Folyamat szimulálása
const interval = setInterval(() => {
progress += 20;
if (progress < 100) {
downloadProgress.textContent = `Letöltés folyamatban: ${progress}%`;
} else {
downloadProgress.textContent = 'Letöltés befejezve! A fájl elérhető a Letöltések mappában.';
downloadProgress.className = 'download-status complete';
clearInterval(interval);
this.disabled = false;
}
}, 1000);
});
// Automatikus mentés példa
const editor = document.getElementById('editor');
const autosaveStatus = document.getElementById('autosave-status');
let autosaveTimeout;
let lastSaved = '';
editor.addEventListener('input', function() {
clearTimeout(autosaveTimeout);
// Mentés ikon megjelenítése
autosaveStatus.textContent = 'Módosítások...';
autosaveStatus.className = 'autosave-indicator unsaved';
// Automatikus mentés 2 másodperc inaktivitás után
autosaveTimeout = setTimeout(() => {
const currentText = editor.value;
if (currentText !== lastSaved) {
// Mentés szimulálása
lastSaved = currentText;
const time = new Date().toLocaleTimeString('hu-HU');
autosaveStatus.textContent = `Automatikusan mentve: ${time}`;
autosaveStatus.className = 'autosave-indicator saved';
// Üzenet elhalványítása
setTimeout(() => {
autosaveStatus.classList.add('fade');
}, 3000);
}
}, 2000);
});
</script>
<style>
.cart-notifications {
margin-top: 8px;
padding: 8px;
background-color: #e3f2fd;
color: #1976d2;
border-radius: 4px;
min-height: 32px;
}
.chat-window {
border: 1px solid #ccc;
height: 200px;
overflow-y: auto;
padding: 8px;
margin-bottom: 8px;
background-color: #f5f5f5;
}
.messages p {
margin: 4px 0;
padding: 4px 8px;
background-color: white;
border-radius: 4px;
}
.download-status {
margin-top: 8px;
padding: 8px;
border-radius: 4px;
font-weight: bold;
}
.download-status.downloading {
background-color: #fff3cd;
color: #856404;
}
.download-status.complete {
background-color: #d4edda;
color: #155724;
}
.autosave-indicator {
margin-top: 4px;
font-size: 14px;
transition: opacity 0.3s;
}
.autosave-indicator.unsaved {
color: #666;
font-style: italic;
}
.autosave-indicator.saved {
color: #28a745;
}
.autosave-indicator.fade {
opacity: 0.5;
}
.sr-only {
position: absolute;
left: -10000px;
width: 1px;
height: 1px;
overflow: hidden;
}
</style>
3. Tömeges műveletek visszajelzései
<!-- FÁJLKEZELŐ ALKALMAZÁS -->
<div class="file-manager">
<h2>Fájlkezelő</h2>
<!-- Műveleti gombok -->
<div class="toolbar">
<button type="button" id="select-all" class="btn-secondary">
Összes kijelölése
</button>
<button type="button" id="delete-selected" class="btn-danger" disabled>
Kijelöltek törlése
</button>
<button type="button" id="move-selected" class="btn-secondary" disabled>
Kijelöltek áthelyezése
</button>
</div>
<!-- Fájl lista -->
<div class="file-list" role="region" aria-label="Fájlok listája">
<div class="file-item">
<input type="checkbox" id="file1" class="file-checkbox">
<label for="file1">dokumentum.pdf</label>
</div>
<div class="file-item">
<input type="checkbox" id="file2" class="file-checkbox">
<label for="file2">képek.zip</label>
</div>
<div class="file-item">
<input type="checkbox" id="file3" class="file-checkbox">
<label for="file3">táblázat.xlsx</label>
</div>
</div>
<!-- Műveleti állapotok -->
<div
id="file-operation-status"
role="status"
aria-live="polite"
aria-atomic="true"
class="operation-status"
></div>
<!-- Visszavonás lehetőség -->
<div
id="undo-region"
role="status"
aria-live="polite"
class="undo-notification"
style="display: none;"
>
<span id="undo-message"></span>
<button type="button" id="undo-action" class="btn-link">
Visszavonás
</button>
</div>
</div>
<!-- KÉPGALÉRIA FELTÖLTÉS -->
<div class="image-gallery">
<h2>Képfeltöltés</h2>
<div class="upload-area">
<input
type="file"
id="file-upload"
multiple
accept="image/*"
class="file-input"
aria-describedby="upload-help upload-status"
>
<label for="file-upload" class="upload-label">
Válasszon képeket a feltöltéshez
</label>
<span id="upload-help" class="help-text">
Több fájl kiválasztásához használja a Ctrl/Cmd billentyűt
</span>
</div>
<!-- Feltöltési állapot -->
<div
id="upload-status"
role="status"
aria-live="polite"
aria-busy="false"
class="upload-progress"
></div>
<!-- Feltöltött képek listája -->
<div
id="uploaded-images"
role="region"
aria-label="Feltöltött képek"
class="image-grid"
></div>
</div>
<script>
// Fájlkezelő funkciók
const fileCheckboxes = document.querySelectorAll('.file-checkbox');
const deleteBtn = document.getElementById('delete-selected');
const moveBtn = document.getElementById('move-selected');
const fileOperationStatus = document.getElementById('file-operation-status');
const undoRegion = document.getElementById('undo-region');
const undoMessage = document.getElementById('undo-message');
let deletedFiles = [];
// Kijelölés kezelése
function updateSelection() {
const selected = document.querySelectorAll('.file-checkbox:checked').length;
deleteBtn.disabled = selected === 0;
moveBtn.disabled = selected === 0;
if (selected > 0) {
fileOperationStatus.textContent = `${selected} fájl kijelölve`;
} else {
fileOperationStatus.textContent = '';
}
}
fileCheckboxes.forEach(checkbox => {
checkbox.addEventListener('change', updateSelection);
});
// Összes kijelölése
document.getElementById('select-all').addEventListener('click', function() {
const allChecked = Array.from(fileCheckboxes).every(cb => cb.checked);
fileCheckboxes.forEach(cb => {
cb.checked = !allChecked;
});
updateSelection();
if (!allChecked) {
fileOperationStatus.textContent = 'Minden fájl kijelölve';
} else {
fileOperationStatus.textContent = 'Kijelölés megszüntetve';
}
});
// Törlés művelet
document.getElementById('delete-selected').addEventListener('click', function() {
const selected = document.querySelectorAll('.file-checkbox:checked');
deletedFiles = [];
selected.forEach(checkbox => {
const fileName = checkbox.nextElementSibling.textContent;
deletedFiles.push({
checkbox: checkbox,
fileName: fileName,
parent: checkbox.parentElement
});
checkbox.parentElement.style.display = 'none';
});
// Állapot frissítése
fileOperationStatus.textContent = `${deletedFiles.length} fájl törölve`;
// Visszavonás lehetőség megjelenítése
undoMessage.textContent = `${deletedFiles.length} fájl törölve. `;
undoRegion.style.display = 'block';
// Gombok frissítése
updateSelection();
// Visszavonás elrejtése 10 másodperc után
setTimeout(() => {
undoRegion.style.display = 'none';
deletedFiles = [];
}, 10000);
});
// Visszavonás
document.getElementById('undo-action').addEventListener('click', function() {
deletedFiles.forEach(file => {
file.parent.style.display = 'block';
file.checkbox.checked = false;
});
fileOperationStatus.textContent = 'Törlés visszavonva';
undoRegion.style.display = 'none';
deletedFiles = [];
});
// Képfeltöltés példa
const fileUpload = document.getElementById('file-upload');
const uploadStatus = document.getElementById('upload-status');
const uploadedImages = document.getElementById('uploaded-images');
fileUpload.addEventListener('change', function(e) {
const files = Array.from(e.target.files);
if (files.length === 0) return;
// Feltöltés indítása
uploadStatus.setAttribute('aria-busy', 'true');
uploadStatus.textContent = `${files.length} kép feltöltése megkezdve...`;
uploadStatus.className = 'upload-progress uploading';
let uploaded = 0;
// Szimulált feltöltés
files.forEach((file, index) => {
setTimeout(() => {
uploaded++;
// Frissítés minden fájl után
if (uploaded < files.length) {
uploadStatus.textContent = `Feltöltés: ${uploaded}/${files.length} kép kész`;
} else {
uploadStatus.setAttribute('aria-busy', 'false');
uploadStatus.textContent = `Minden kép sikeresen feltöltve (${files.length} fájl)`;
uploadStatus.className = 'upload-progress complete';
// Feltöltött képek megjelenítése
const img = document.createElement('div');
img.className = 'uploaded-image';
img.textContent = `${files.length} új kép`;
uploadedImages.appendChild(img);
}
}, (index + 1) * 1000);
});
// Input törlése
this.value = '';
});
</script>
<style>
.operation-status {
margin: 8px 0;
padding: 8px;
background-color: #e8f5e9;
color: #2e7d32;
border-radius: 4px;
min-height: 32px;
}
.undo-notification {
margin-top: 8px;
padding: 8px;
background-color: #fff3cd;
border: 1px solid #ffeeba;
border-radius: 4px;
}
.file-item {
padding: 4px 0;
}
.upload-area {
border: 2px dashed #ccc;
padding: 20px;
text-align: center;
margin-bottom: 16px;
}
.file-input {
display: none;
}
.upload-label {
display: inline-block;
padding: 8px 16px;
background-color: #007bff;
color: white;
border-radius: 4px;
cursor: pointer;
}
.upload-label:hover {
background-color: #0056b3;
}
.upload-progress {
margin-top: 8px;
padding: 8px;
border-radius: 4px;
text-align: center;
}
.upload-progress.uploading {
background-color: #cfe2ff;
color: #084298;
}
.upload-progress.complete {
background-color: #d1e7dd;
color: #0f5132;
}
.image-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 8px;
margin-top: 16px;
}
.uploaded-image {
padding: 20px;
background-color: #f0f0f0;
text-align: center;
border-radius: 4px;
}
</style>
Rossz gyakorlatok
Kerülendő megoldások:
- Fókuszt eltoló figyelmeztetések használata: A role=”alert” túl erős figyelmeztetés és elmozdítja a fókuszt, ami zavaró lehet, ha a felhasználó éppen gépel vagy navigál. Inkább a role=”status” használata az ajánlottabb a frissítésekhez, ami nem annyira zavaró
- Tartalom frissítése ARIA élő régiók nélkül: Az ARIA élő attribútumok nélküli frissítések nem kerülnek bejelentésre a képernyőolvasók által
- Billentyűzet fókusz szükségtelen áthelyezése állapotüzenetekre: A fókusz megváltoztatása összezavarhatja a billentyűzetes és képernyőolvasó felhasználókat
- Túl gyakori vagy zajos állapotfrissítések: Minden apró változás bejelentése túlterhelheti a felhasználókat
1. Helytelen „alert” használat bemutatása
<!-- ROSSZ GYAKORLAT - Tolakodó alert használata -->
<form>
<input type="email" id="email-input">
<div id="alert" role="alert" style="display: none;"></div>
</form>
<script>
document.getElementById('email-input').addEventListener('input', function() {
const alertDiv = document.getElementById('alert');
if (!this.value.includes('@')) {
alertDiv.textContent = 'Hiba: Érvénytelen e-mail formátum!';
alertDiv.style.display = 'block';
// ROSSZ: Az alert megszakítja a felhasználót és elmozdítja a fókuszt
// Normál gépelés közben ez zavaró
}
});
</script>
2. Tartalom frissítése ARIA élő régiók nélkül
<!-- ROSSZ GYAKORLAT - Élő régió nélküli frissítés -->
<div id="status"></div>
<button onclick="updateStatus()">Állapot frissítése</button>
<script>
function updateStatus() {
// Ez nem lesz bejelentve képernyőolvasók által
document.getElementById('status').textContent = 'Frissítés elérhető.';
// Hiányzik: role="status" vagy aria-live="polite"
}
</script>
<!-- ROSSZ GYAKORLAT - Dinamikus értesítés megfelelő jelölés nélkül -->
<div class="notification-area">
<!-- Nincs ARIA live régió -->
</div>
<script>
function showNotification(message) {
const notificationArea = document.querySelector('.notification-area');
const notification = document.createElement('div');
notification.textContent = message;
notificationArea.appendChild(notification);
// A képernyőolvasó nem értesül az új üzenetről
}
</script>
3. Fókusz szükségtelen áthelyezése az állapotüzenetekre
<!-- ROSSZ GYAKORLAT - Fókusz áthelyezése -->
<form>
<input type="text" id="name-input">
<div id="message" tabindex="-1" style="display: none;"></div>
</form>
<script>
function showMessage(text) {
const msg = document.getElementById('message');
msg.textContent = text;
msg.style.display = 'block';
msg.focus(); // ROSSZ: Ne mozgasd a fókuszt állapotüzenetekre
// A felhasználó elveszíti a helyét az űrlapban
}
document.getElementById('name-input').addEventListener('blur', function() {
if (this.value.length < 3) {
showMessage('A név túl rövid!');
// A fókusz elmozdul, ami zavaró
}
});
</script>
<!-- ROSSZ GYAKORLAT - Automatikus fókusz értesítésekre -->
<div class="toast-container"></div>
<script>
function showToast(message) {
const container = document.querySelector('.toast-container');
const toast = document.createElement('div');
toast.className = 'toast';
toast.textContent = message;
toast.tabIndex = -1;
container.appendChild(toast);
// ROSSZ: Fókusz elmozdítása az értesítésre
toast.focus();
setTimeout(() => {
container.removeChild(toast);
}, 3000);
}
</script>
4. Túl gyakori vagy zavaró állapotfrissítések
<!-- ROSSZ GYAKORLAT - Túl gyakori frissítések -->
<div role="status" aria-live="polite" id="counter"></div>
<div role="status" aria-live="polite" id="timer"></div>
<script>
let count = 0;
setInterval(() => {
// Minden másodpercben frissít - túl zajos
document.getElementById('counter').textContent = `Számláló: ${count++}`;
}, 1000);
// Még egy másodpercenkénti frissítés
setInterval(() => {
const now = new Date();
document.getElementById('timer').textContent = `Idő: ${now.toLocaleTimeString()}`;
// Ez is túl gyakori és zavaró
}, 1000);
</script>
<!-- ROSSZ GYAKORLAT - Minden apró változás bejelentése -->
<input type="text" id="search-input">
<div role="status" aria-live="polite" id="search-status"></div>
<script>
document.getElementById('search-input').addEventListener('input', function() {
const searchStatus = document.getElementById('search-status');
// ROSSZ: Minden karakternél frissít
searchStatus.textContent = `${this.value.length} karakter beírva`;
// Ez túlságosan zajos a képernyőolvasóknál
});
</script>
5. Nem frissülnek az automatikus összetett üzenetek
<!-- ROSSZ GYAKORLAT - Nem automatikus frissítések -->
<div role="status" aria-live="polite">
<span id="part1">Állapot:</span>
<span id="part2"></span>
<span id="part3"></span>
</div>
<script>
function updateStatus(status, progress) {
// ROSSZ: Részletes frissítések külön-külön
document.getElementById('part2').textContent = status;
setTimeout(() => {
document.getElementById('part3').textContent = ` - ${progress}%`;
// A képernyőolvasó csak részeket hall, összezavaró lehet
}, 100);
}
</script>
<!-- ROSSZ GYAKORLAT - Többszörös élő régiók egyszerre -->
<div role="status" aria-live="polite" id="status1"></div>
<div role="status" aria-live="polite" id="status2"></div>
<div role="status" aria-live="polite" id="status3"></div>
<script>
function showMultipleMessages() {
// ROSSZ: Egyszerre több üzenet - zavaró
document.getElementById('status1').textContent = 'Első üzenet';
document.getElementById('status2').textContent = 'Második üzenet';
document.getElementById('status3').textContent = 'Harmadik üzenet';
// A képernyőolvasó egyszerre próbálja bejelenteni mindet
}
</script>
6. Hiányzó aria-atomic attribútum összetett tartalmaknál
<!-- ROSSZ GYAKORLAT - Hiányzó aria-atomic -->
<div role="status" aria-live="polite" id="complex-status">
<span class="icon">✓</span>
<span class="message">Sikeres művelet</span>
<span class="details">5 elemből 3 feldolgozva</span>
</div>
<script>
function updateComplexStatus(message, details) {
const statusDiv = document.getElementById('complex-status');
// ROSSZ: Részleges frissítés aria-atomic nélkül
statusDiv.querySelector('.message').textContent = message;
statusDiv.querySelector('.details').textContent = details;
// A képernyőolvasó lehet hogy csak a változott részt mondja be
// Hiányzik: aria-atomic="true"
}
</script>
<!-- ROSSZ GYAKORLAT - Strukturált tartalom rossz kezelése -->
<div role="status" aria-live="polite">
<ul id="status-list">
<li>Állandó elem</li>
</ul>
</div>
<script>
function addStatusItem(text) {
const list = document.getElementById('status-list');
const newItem = document.createElement('li');
newItem.textContent = text;
list.appendChild(newItem);
// Lehet hogy csak az új elem kerül bejelentésre, kontextus nélkül
}
</script>
Források
Iratkozz fel hírlevelünkre!
Amennyiben szeretnél első kézből értesülni az új bejegyzésekről, iratkozz fel hírlevelünkre!