2.1.2 - Billentyűzetcsapda nélkül
Röviden a szabványpontról
A WCAG 2.1.2 (No Keyboard Trap) előírja, hogy amikor a billentyűzet fókusz egy oldal komponensére kerül billentyűzetes interfész használatával, a felhasználónak képesnek kell lennie a fókusz elmozgatására arról a komponensről csak billentyűzet használatával. Ez minden olyan webes tartalomra vonatkozik, amely billentyűzettel navigálható vagy kezelhető.
Cél: Biztosítani, hogy a kizárólag billentyűzetre támaszkodó felhasználók szabadon navigálhassanak anélkül, hogy elakadnának, megelőzve a frusztrációt és az irányítás elvesztését.
Kiket érint
Elsődleges felhasználók: Mozgássérült emberek, akik nem tudnak egeret használni és teljes mértékben billentyűzetes navigációra vagy billentyűzetet emuláló alternatív beviteli eszközökre támaszkodnak.
Másodlagos előnyök: Javítja az általános billentyűzetes navigációs élményt minden felhasználó számára, beleértve a haladó felhasználókat és a kisegítő technológiákat használókat, mint képernyőolvasók vagy hangvezérlés.
Tesztelés
- Csak billentyűzetes navigációs teszt: Használd a Tab billentyűt (és Shift+Tab) a fókusz mozgatásához az oldal összes interaktív elemén keresztül. Ellenőrizd, hogy minden komponensből ki tudsz lépni egér használata nélkül
- Csapda észlelés: Amikor a fókusz egy widgeten belül van (pl. modális dialógus, egyedi legördülő), próbáld kimozgatni a fókuszt gyakori billentyűk használatával, mint Tab, Shift+Tab vagy Escape. A fókusz nem lehet csapdában
- Kisegítő technológiák tesztelése: Teszteld a billentyűzetes navigációt képernyőolvasókkal és hangvezérléssel, hogy biztosítsd, hogy nem történnek csapdák
- Automatizált tesztelés: Használj eszközöket, mint az axe DevTools a 2.1.2 szabály által jelzett billentyűzet csapda problémák észlelésére
- Manuális kód áttekintés: Vizsgáld meg a billentyűzet fókuszt kezelő JavaScript eseménykezelőket, hogy biztosítsd, hogy nem blokkolják a fókusz elmozgatását
Jó gyakorlatok
1. Modális dialógusok megfelelő kezelése
<div class="modal-example">
<button id="open-modal-btn" class="open-button">Modál megnyitása</button>
<div role="dialog" aria-modal="true" aria-labelledby="modal-title" id="accessible-modal" class="modal" hidden>
<div class="modal-content">
<h2 id="modal-title">Hozzáférhető modál</h2>
<p>Ez egy megfelelően implementált modál, amelyből könnyen ki lehet lépni billentyűzettel.</p>
<div class="modal-form">
<label for="modal-input">Példa mező:</label>
<input type="text" id="modal-input" placeholder="Írd be a szöveget">
</div>
<div class="modal-actions">
<button id="save-btn" class="primary-btn">Mentés</button>
<button id="close-modal-btn" class="secondary-btn">Bezárás</button>
</div>
</div>
</div>
</div>
<style>
.modal-example {
margin: 20px;
}
.open-button {
background-color: #007bff;
color: white;
border: none;
padding: 12px 24px;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
.open-button:hover, .open-button:focus {
background-color: #0056b3;
outline: 2px solid #80bdff;
outline-offset: 2px;
}
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.modal[hidden] {
display: none;
}
.modal-content {
background-color: white;
padding: 30px;
border-radius: 8px;
max-width: 500px;
width: 90%;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
position: relative;
}
.modal-form {
margin: 20px 0;
}
.modal-form label {
display: block;
margin-bottom: 8px;
font-weight: bold;
}
.modal-form input {
width: 100%;
padding: 10px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
font-size: 16px;
}
.modal-form input:focus {
outline: 2px solid #007bff;
outline-offset: 2px;
border-color: #007bff;
}
.modal-actions {
display: flex;
gap: 10px;
justify-content: flex-end;
margin-top: 20px;
}
.primary-btn {
background-color: #28a745;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
}
.secondary-btn {
background-color: #6c757d;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
}
.primary-btn:hover, .primary-btn:focus,
.secondary-btn:hover, .secondary-btn:focus {
outline: 2px solid #fff;
outline-offset: 2px;
}
.primary-btn:hover { background-color: #218838; }
.secondary-btn:hover { background-color: #545b62; }
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
const openModalBtn = document.getElementById('open-modal-btn');
const modal = document.getElementById('accessible-modal');
const closeModalBtn = document.getElementById('close-modal-btn');
const saveBtn = document.getElementById('save-btn');
const modalInput = document.getElementById('modal-input');
let lastFocusedElement;
let modalFocusableElements;
let firstFocusableElement;
let lastFocusableElement;
function getFocusableElements() {
modalFocusableElements = modal.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
firstFocusableElement = modalFocusableElements[0];
lastFocusableElement = modalFocusableElements[modalFocusableElements.length - 1];
}
function openModal() {
// Aktuális fókusz mentése
lastFocusedElement = document.activeElement;
// Modál megjelenítése
modal.hidden = false;
// Fókuszálható elemek frissítése
getFocusableElements();
// Fókusz az első elemre (input mező)
modalInput.focus();
// Eseménykezelő hozzáadása a fókusz csapdázásához
modal.addEventListener('keydown', handleModalKeydown);
}
function closeModal() {
// Modál elrejtése
modal.hidden = true;
// Eseménykezelő eltávolítása
modal.removeEventListener('keydown', handleModalKeydown);
// Fókusz visszaadása az eredeti elemnek
if (lastFocusedElement) {
lastFocusedElement.focus();
}
}
function handleModalKeydown(e) {
// Escape billentyűvel bezárás
if (e.key === 'Escape') {
e.preventDefault();
closeModal();
return;
}
// Tab billentyű kezelése a fókusz csapdázásához
if (e.key === 'Tab') {
if (e.shiftKey) {
// Shift + Tab: hátrafelé
if (document.activeElement === firstFocusableElement) {
e.preventDefault();
lastFocusableElement.focus();
}
} else {
// Tab: előre
if (document.activeElement === lastFocusableElement) {
e.preventDefault();
firstFocusableElement.focus();
}
}
}
}
// Esemény figyelők
openModalBtn.addEventListener('click', openModal);
closeModalBtn.addEventListener('click', closeModal);
saveBtn.addEventListener('click', function() {
alert('Adatok mentve!');
closeModal();
});
// Háttérre kattintás
modal.addEventListener('click', function(e) {
if (e.target === modal) {
closeModal();
}
});
});
</script>
Magyarázat: Az Escape billentyű megnyomása bezárja a modált és visszaadja a fókuszt az azt megnyitó elemnek. A fókusz megfelelően csapdázódik a modálban, de mindig van kilépési lehetőség.
2. Egyedi legördülő menü megfelelő kilépési lehetőségekkel
<div class="dropdown-example">
<h3>Egyedi legördülő menü</h3>
<div class="dropdown-container">
<button id="dropdown-trigger" aria-haspopup="listbox" aria-expanded="false" class="dropdown-button">
Válassz opciót ▼
</button>
<ul id="dropdown-menu" role="listbox" class="dropdown-menu" hidden>
<li role="option" tabindex="0" data-value="option1">Első opció</li>
<li role="option" tabindex="0" data-value="option2">Második opció</li>
<li role="option" tabindex="0" data-value="option3">Harmadik opció</li>
<li role="option" tabindex="0" data-value="option4">Negyedik opció</li>
</ul>
</div>
</div>
<style>
.dropdown-example {
margin: 20px;
max-width: 300px;
}
.dropdown-container {
position: relative;
}
.dropdown-button {
width: 100%;
padding: 12px 16px;
background-color: white;
border: 2px solid #ced4da;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
text-align: left;
display: flex;
justify-content: space-between;
align-items: center;
}
.dropdown-button:hover, .dropdown-button:focus {
border-color: #007bff;
outline: 2px solid #80bdff;
outline-offset: 2px;
}
.dropdown-button[aria-expanded="true"] {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
border-bottom-color: transparent;
}
.dropdown-menu {
position: absolute;
top: 100%;
left: 0;
right: 0;
background-color: white;
border: 2px solid #007bff;
border-top: none;
border-radius: 0 0 4px 4px;
list-style: none;
margin: 0;
padding: 0;
z-index: 1000;
max-height: 200px;
overflow-y: auto;
}
.dropdown-menu[hidden] {
display: none;
}
.dropdown-menu li {
padding: 12px 16px;
cursor: pointer;
border-bottom: 1px solid #eee;
}
.dropdown-menu li:last-child {
border-bottom: none;
}
.dropdown-menu li:hover,
.dropdown-menu li:focus {
background-color: #f8f9fa;
outline: 2px solid #007bff;
outline-offset: -2px;
}
.dropdown-menu li[aria-selected="true"] {
background-color: #007bff;
color: white;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
const dropdownTrigger = document.getElementById('dropdown-trigger');
const dropdownMenu = document.getElementById('dropdown-menu');
const options = dropdownMenu.querySelectorAll('[role="option"]');
let isOpen = false;
let currentIndex = -1;
function openDropdown() {
isOpen = true;
dropdownMenu.hidden = false;
dropdownTrigger.setAttribute('aria-expanded', 'true');
// Első opcióra fókusz
if (options.length > 0) {
currentIndex = 0;
options[currentIndex].focus();
}
}
function closeDropdown() {
isOpen = false;
dropdownMenu.hidden = true;
dropdownTrigger.setAttribute('aria-expanded', 'false');
dropdownTrigger.focus();
currentIndex = -1;
}
function selectOption(index) {
const selectedOption = options[index];
const value = selectedOption.getAttribute('data-value');
const text = selectedOption.textContent;
// Kiválasztott opció jelölése
options.forEach(option => option.setAttribute('aria-selected', 'false'));
selectedOption.setAttribute('aria-selected', 'true');
// Gomb szövegének frissítése
dropdownTrigger.innerHTML = text + ' ▼';
closeDropdown();
}
// Trigger gomb események
dropdownTrigger.addEventListener('click', function() {
if (isOpen) {
closeDropdown();
} else {
openDropdown();
}
});
dropdownTrigger.addEventListener('keydown', function(e) {
switch(e.key) {
case 'Enter':
case ' ':
case 'ArrowDown':
e.preventDefault();
if (!isOpen) {
openDropdown();
}
break;
case 'ArrowUp':
e.preventDefault();
if (!isOpen) {
openDropdown();
}
break;
case 'Escape':
if (isOpen) {
e.preventDefault();
closeDropdown();
}
break;
}
});
// Opciók eseménykezelése
options.forEach((option, index) => {
option.addEventListener('click', function() {
selectOption(index);
});
option.addEventListener('keydown', function(e) {
switch(e.key) {
case 'Enter':
case ' ':
e.preventDefault();
selectOption(index);
break;
case 'ArrowDown':
e.preventDefault();
currentIndex = (index + 1) % options.length;
options[currentIndex].focus();
break;
case 'ArrowUp':
e.preventDefault();
currentIndex = (index - 1 + options.length) % options.length;
options[currentIndex].focus();
break;
case 'Escape':
e.preventDefault();
closeDropdown();
break;
case 'Tab':
// Tab billentyű természetes viselkedése - kilép a dropdown-ból
closeDropdown();
break;
}
});
});
// Kívülre kattintás
document.addEventListener('click', function(e) {
if (!dropdownTrigger.contains(e.target) && !dropdownMenu.contains(e.target)) {
if (isOpen) {
closeDropdown();
}
}
});
});
</script>
Magyarázat: Az egyedi legördülő menük esetén a felhasználók Tab billentyűvel természetesen ki tudnak lépni, és billentyűparancsok állnak rendelkezésre a bezáráshoz vagy kilépéshez.
3. Fókusz kezelés csapda nélkül
<div class="focus-management-example">
<h3>Többlépéses varázsló</h3>
<div class="wizard-container">
<div class="wizard-steps">
<span class="step active">1. Személyes adatok</span>
<span class="step">2. Elérhetőség</span>
<span class="step">3. Megerősítés</span>
</div>
<form class="wizard-form">
<div id="step1" class="wizard-step active">
<h4>Személyes adatok</h4>
<label for="firstName">Keresztnév:</label>
<input type="text" id="firstName" name="firstName">
<label for="lastName">Vezetéknév:</label>
<input type="text" id="lastName" name="lastName">
</div>
<div id="step2" class="wizard-step" hidden>
<h4>Elérhetőség</h4>
<label for="email">E-mail:</label>
<input type="email" id="email" name="email">
<label for="phone">Telefon:</label>
<input type="tel" id="phone" name="phone">
</div>
<div id="step3" class="wizard-step" hidden>
<h4>Megerősítés</h4>
<p>Kérlek ellenőrizd az adatokat a folytatás előtt.</p>
<div id="summary"></div>
</div>
<div class="wizard-actions">
<button type="button" id="prevBtn" disabled>Előző</button>
<button type="button" id="nextBtn">Következő</button>
<button type="button" id="exitBtn">Kilépés</button>
</div>
</form>
</div>
</div>
<style>
.focus-management-example {
margin: 20px;
max-width: 500px;
}
.wizard-container {
border: 1px solid #dee2e6;
border-radius: 8px;
padding: 20px;
background-color: #f8f9fa;
}
.wizard-steps {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 1px solid #dee2e6;
}
.step {
padding: 8px 12px;
background-color: #e9ecef;
border-radius: 4px;
font-size: 14px;
flex: 1;
text-align: center;
margin: 0 2px;
}
.step.active {
background-color: #007bff;
color: white;
}
.wizard-form {
background-color: white;
padding: 20px;
border-radius: 4px;
}
.wizard-step label {
display: block;
margin: 10px 0 5px 0;
font-weight: bold;
}
.wizard-step input {
width: 100%;
padding: 8px;
border: 1px solid #ced4da;
border-radius: 4px;
box-sizing: border-box;
margin-bottom: 10px;
}
.wizard-step input:focus {
outline: 2px solid #007bff;
outline-offset: 2px;
border-color: #007bff;
}
.wizard-actions {
display: flex;
gap: 10px;
justify-content: space-between;
margin-top: 20px;
}
.wizard-actions button {
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
}
#prevBtn {
background-color: #6c757d;
color: white;
}
#nextBtn {
background-color: #007bff;
color: white;
}
#exitBtn {
background-color: #dc3545;
color: white;
}
.wizard-actions button:disabled {
background-color: #e9ecef;
color: #6c757d;
cursor: not-allowed;
}
.wizard-actions button:not(:disabled):hover,
.wizard-actions button:not(:disabled):focus {
outline: 2px solid #fff;
outline-offset: 2px;
}
#prevBtn:not(:disabled):hover { background-color: #545b62; }
#nextBtn:not(:disabled):hover { background-color: #0056b3; }
#exitBtn:hover { background-color: #c82333; }
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
const steps = document.querySelectorAll('.wizard-step');
const stepIndicators = document.querySelectorAll('.step');
const prevBtn = document.getElementById('prevBtn');
const nextBtn = document.getElementById('nextBtn');
const exitBtn = document.getElementById('exitBtn');
let currentStep = 0;
const totalSteps = steps.length;
function showStep(stepIndex) {
// Minden lépés elrejtése
steps.forEach((step, index) => {
step.hidden = index !== stepIndex;
stepIndicators[index].classList.toggle('active', index === stepIndex);
});
// Gombok állapotának frissítése
prevBtn.disabled = stepIndex === 0;
nextBtn.textContent = stepIndex === totalSteps - 1 ? 'Befejezés' : 'Következő';
// Fókusz az első beviteli mezőre az aktuális lépésben
const firstInput = steps[stepIndex].querySelector('input');
if (firstInput) {
// Kis késleltetés a DOM frissítéséhez
setTimeout(() => firstInput.focus(), 100);
}
}
function nextStep() {
if (currentStep < totalSteps - 1) {
currentStep++;
showStep(currentStep);
} else {
// Utolsó lépés - befejezés
alert('Adatok sikeresen elmentve!');
exitWizard();
}
}
function prevStep() {
if (currentStep > 0) {
currentStep--;
showStep(currentStep);
}
}
function exitWizard() {
// Varázsló bezárása és fókusz visszaadása
if (confirm('Biztosan ki akarsz lépni? A nem mentett adatok elvesznek.')) {
// Itt lehet a varázslót elrejteni vagy visszairányítani
alert('Kilépés a varázslóból');
// A valóságban itt fókusz visszaadása az eredeti elemre
document.body.focus();
}
}
// Eseménykezelők
nextBtn.addEventListener('click', nextStep);
prevBtn.addEventListener('click', prevStep);
exitBtn.addEventListener('click', exitWizard);
// Billentyűzetes kezelés
document.addEventListener('keydown', function(e) {
// Escape billentyű - kilépés
if (e.key === 'Escape') {
exitWizard();
}
// Ctrl+Enter - gyors továbblépés
if (e.ctrlKey && e.key === 'Enter') {
nextStep();
}
});
// Tab kezelés - természetes navigáció engedélyezése
document.addEventListener('keydown', function(e) {
if (e.key === 'Tab') {
// A Tab billentyű természetes viselkedése - NEM blokkoljuk
// A felhasználó ki tud lépni a varázslóból Tab-bal
}
});
// Kezdeti állapot
showStep(currentStep);
});
</script>
Magyarázat: Kerüld a JavaScript-et, amely fókuszt csapdáz elemeken belül világos kilépési út nélkül. Mindig biztosíts alternatív billentyűzetes vezérlőket és természetes navigációs lehetőségeket.
4. Alternatív navigációs lehetőségek biztosítása
<div class="alternative-navigation">
<h3>Komplex interaktív térkép</h3>
<div class="map-container">
<div id="interactive-map" class="map-area" tabindex="0" role="application" aria-label="Interaktív térkép">
<div class="map-overlay">
<p>Használd a nyílbillentyűket a navigációhoz, Space-t nagyításhoz, Escape-t a kilépéshez.</p>
<div class="map-position">Pozíció: <span id="position">Központ</span></div>
</div>
</div>
<div class="map-controls">
<h4>Alternatív vezérlők</h4>
<button id="center-map">Központ</button>
<button id="zoom-in">Nagyítás</button>
<button id="zoom-out">Kicsinyítés</button>
<button id="exit-map">Kilépés a térképből</button>
</div>
<div class="map-list">
<h4>Helyszínek listája (alternatív hozzáférés)</h4>
<ul>
<li><a href="#location1">Központi park</a></li>
<li><a href="#location2">Múzeum</a></li>
<li><a href="#location3">Könyvtár</a></li>
<li><a href="#location4">Piac</a></li>
</ul>
</div>
</div>
</div>
<style>
.alternative-navigation {
margin: 20px;
max-width: 600px;
}
.map-container {
border: 1px solid #dee2e6;
border-radius: 8px;
overflow: hidden;
}
.map-area {
width: 100%;
height: 300px;
background: linear-gradient(135deg, #74b9ff, #0984e3);
position: relative;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 18px;
}
.map-area:focus {
outline: 4px solid #fff;
outline-offset: -4px;
}
.map-overlay {
text-align: center;
background-color: rgba(0, 0, 0, 0.7);
padding: 20px;
border-radius: 8px;
}
.map-position {
margin-top: 10px;
font-weight: bold;
}
.map-controls {
padding: 15px;
background-color: #f8f9fa;
border-bottom: 1px solid #dee2e6;
}
.map-controls h4 {
margin: 0 0 10px 0;
font-size: 14px;
}
.map-controls button {
margin-right: 10px;
margin-bottom: 5px;
padding: 8px 16px;
border: 1px solid #ced4da;
background-color: white;
border-radius: 4px;
cursor: pointer;
}
.map-controls button:hover,
.map-controls button:focus {
background-color: #e9ecef;
outline: 2px solid #007bff;
outline-offset: 2px;
}
#exit-map {
background-color: #dc3545;
color: white;
border-color: #dc3545;
}
#exit-map:hover,
#exit-map:focus {
background-color: #c82333;
}
.map-list {
padding: 15px;
background-color: white;
}
.map-list h4 {
margin: 0 0 10px 0;
font-size: 14px;
}
.map-list ul {
list-style: none;
padding: 0;
margin: 0;
}
.map-list li {
margin-bottom: 5px;
}
.map-list a {
display: block;
padding: 8px 12px;
color: #007bff;
text-decoration: none;
border-radius: 4px;
border: 1px solid transparent;
}
.map-list a:hover,
.map-list a:focus {
background-color: #f8f9fa;
border-color: #007bff;
outline: 2px solid #007bff;
outline-offset: -2px;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
const mapArea = document.getElementById('interactive-map');
const positionDisplay = document.getElementById('position');
const centerBtn = document.getElementById('center-map');
const zoomInBtn = document.getElementById('zoom-in');
const zoomOutBtn = document.getElementById('zoom-out');
const exitBtn = document.getElementById('exit-map');
let mapPosition = { x: 0, y: 0 };
let zoomLevel = 1;
let isMapFocused = false;
function updatePosition() {
let positionText = `X: ${mapPosition.x}, Y: ${mapPosition.y}, Zoom: ${zoomLevel}x`;
if (mapPosition.x === 0 && mapPosition.y === 0) {
positionText = 'Központ';
}
positionDisplay.textContent = positionText;
}
function exitMap() {
// Fókusz elmozgatása a térképről
exitBtn.focus();
isMapFocused = false;
// Vizuális jelzés a kilépésről
mapArea.style.opacity = '0.7';
setTimeout(() => {
mapArea.style.opacity = '1';
}, 200);
}
// Térkép fókusz események
mapArea.addEventListener('focus', function() {
isMapFocused = true;
});
mapArea.addEventListener('blur', function() {
isMapFocused = false;
});
// Térkép billentyűzetes vezérlés
mapArea.addEventListener('keydown', function(e) {
if (!isMapFocused) return;
switch(e.key) {
case 'ArrowUp':
e.preventDefault();
mapPosition.y = Math.max(-10, mapPosition.y - 1);
updatePosition();
break;
case 'ArrowDown':
e.preventDefault();
mapPosition.y = Math.min(10, mapPosition.y + 1);
updatePosition();
break;
case 'ArrowLeft':
e.preventDefault();
mapPosition.x = Math.max(-10, mapPosition.x - 1);
updatePosition();
break;
case 'ArrowRight':
e.preventDefault();
mapPosition.x = Math.min(10, mapPosition.x + 1);
updatePosition();
break;
case ' ':
e.preventDefault();
zoomLevel = Math.min(5, zoomLevel + 0.5);
updatePosition();
break;
case 'Enter':
e.preventDefault();
zoomLevel = Math.max(0.5, zoomLevel - 0.5);
updatePosition();
break;
case 'Escape':
e.preventDefault();
exitMap();
break;
case 'Tab':
// Tab billentyű természetes viselkedése - kilépés a térképből
exitMap();
break;
}
});
// Alternatív vezérlő gombok
centerBtn.addEventListener('click', function() {
mapPosition = { x: 0, y: 0 };
zoomLevel = 1;
updatePosition();
mapArea.focus();
});
zoomInBtn.addEventListener('click', function() {
zoomLevel = Math.min(5, zoomLevel + 0.5);
updatePosition();
});
zoomOutBtn.addEventListener('click', function() {
zoomLevel = Math.max(0.5, zoomLevel - 0.5);
updatePosition();
});
exitBtn.addEventListener('click', exitMap);
// Helyszín linkek
document.querySelectorAll('.map-list a').forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
const locationName = this.textContent;
alert(`Navigálás: ${locationName}`);
// Itt lehetne a térkép pozícióját beállítani
});
});
// Kezdeti állapot
updatePosition();
});
</script>
Magyarázat: Komplex billentyűzetes interakciókat igénylő funkciók esetén biztosíts alternatív billentyűzetes hozzáférhető vezérlőket és mindig legyen egyszerű kilépési lehetőség.
Rossz gyakorlatok
Fókusz csapdázása
<!-- ROSSZ PÉLDA: Tab billentyű blokkolása -->
<div id="bad-trap-container" tabindex="0">
<p>Ez egy rossz példa, ahol a fókusz csapdába esik</p>
<button>Gomb</button>
</div>
<script>
// ROSSZ: Tab billentyű blokkolása a fókusz elem belsejében tartásához
document.getElementById('bad-trap-container').addEventListener('keydown', function(e) {
if (e.key === 'Tab') {
e.preventDefault(); // Fókusz csapdázása
// A felhasználó nem tud kilépni innen
}
});
</script>
Probléma: A Tab vagy Shift+Tab megakadályozása csapdába ejti a billentyűzet felhasználókat, akik nem tudnak kilépni a komponensből.
Escape billentyű kezelés hiánya
<!-- ROSSZ PÉLDA: Modál Escape kezelés nélkül -->
<div id="bad-modal" class="modal" style="display: block;">
<div class="modal-content">
<p>Ez a modál nem zárható be billentyűzettel</p>
<!-- Nincs billentyűzetes bezárási lehetőség -->
</div>
</div>
<script>
// ROSSZ: Nincs Escape kezelés
document.getElementById('bad-modal').addEventListener('keydown', function(e) {
// Escape billentyű figyelmen kívül hagyása
// Nincs módja a modál bezárásának billentyűzettel
});
</script>
Probléma: Modálok vagy overlay-ek, amelyek nem zárhatók be vagy nem lehet belőlük kilépni billentyűzettel, akadálymentességi problémát okoznak.
Rejtett fókuszálható elemek
<!-- ROSSZ PÉLDA: Láthatatlan elemek a tab sorrendben -->
<style>
.hidden-but-focusable {
position: absolute;
left: -9999px; /* Képernyőn kívül */
/* VAGY */
opacity: 0; /* Láthatatlan de fókuszálható */
}
</style>
<div>
<button>Látható gomb</button>
<button class="hidden-but-focusable">Rejtett de fókuszálható</button>
<button>Másik látható gomb</button>
</div>
<script>
// ROSSZ: Fókusz mozgatása láthatatlan elemekre
function badFocusToHidden() {
document.querySelector('.hidden-but-focusable').focus();
// A felhasználó nem tudja, hol van a fókusz
}
</script>
Probléma: A fókusz láthatatlan vagy képernyőn kívüli elemekre mozgatása megzavarja a felhasználókat és fókusz csapdát okozhat.
Komplex billentyűparancsok alternatívák nélkül
<!-- ROSSZ PÉLDA: Csak komplex billentyűkombinációk -->
<div id="complex-widget" tabindex="0">
<p>Komplex widget - csak Ctrl+Shift+Alt+billentyű kombinációkkal működik</p>
</div>
<script>
// ROSSZ: Csak nehéz billentyűkombinációk
document.getElementById('complex-widget').addEventListener('keydown', function(e) {
// Csak bonyolult kombinációk
if (e.ctrlKey && e.shiftKey && e.altKey && e.key === 'X') {
// Kilépés csak ezzel a nehéz kombinációval
this.blur();
}
// Nincs egyszerű alternatíva, mint Escape vagy Tab
// Tab blokkolása
if (e.key === 'Tab') {
e.preventDefault(); // Csapda!
}
});
</script>
Probléma: Olyan szekvenciák megkövetelése, amelyeket nehéz replikálni vagy lehetetlenné teszik a kilépést, akadálymentességi akadályokat hoz létre.
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!