2.2.1 - Időkorlátok
Röviden a szabványpontról
A WCAG 2.2.1 (Timing Adjustable) előírja, hogy a felhasználóknak elegendő időt kell biztosítani a tartalom elolvasására és használatára. Ez minden olyan tartalomra vagy funkcionalitásra vonatkozik, amely időkorláttal vagy időérzékeny interakciókkal rendelkezik. A cél az, hogy azok a felhasználók, akiknek több időre lehet szükségük – kognitív, mozgásszervi vagy egyéb fogyatékosság miatt – ne legyenek igazságtalanul korlátozva vagy kényszerítve a feladatok sietős elvégzésére. Ez magában foglalja az űrlapokat, kvízeket, időtúllépéseket vagy bármilyen időzített folyamatot egy weboldalon vagy alkalmazásban.
Kiket érint
Elsődleges felhasználók: Kognitív fogyatékossággal, mozgássérüléssel élő emberek, vagy azok, akik az interakciót lassító kisegítő technológiákat használnak (pl. képernyőolvasók, kapcsoló eszközök).
Másodlagos előnyök: Stressz alatt lévő felhasználók, átmeneti fogyatékossággal vagy helyzetfüggő korlátozásokkal rendelkezők (mint erős napfény vagy figyelemelterelés), és idősebb felnőttek, akiknek több időre lehet szükségük az információ feldolgozásához.
Tesztelés
- Időzített tartalom azonosítása: Ellenőrizd, van-e bármilyen tartalom vagy interaktív elem, amely időkorláttal rendelkezik (pl. automatikus kijelentkezés, kvízek, űrlap időtúllépések)
- Módosítható időzítés tesztelése: Ellenőrizd, hogy a felhasználók meghosszabbíthatják, módosíthatják vagy kikapcsolhatják-e az időkorlátokat
- Billentyűzet és képernyőolvasó tesztelés: Használj csak billentyűzetes és képernyőolvasós navigációt annak biztosítására, hogy az időmódosítások hozzáférhetőek
- Lassabb interakciók szimulálása: Használj eszközöket vagy manuális tesztelést a lassabb bevitel szimulálására és erősítsd meg, hogy a tartalom nem időzít ki idő előtt
- Szkriptek áttekintése: Vizsgáld meg a JavaScript vagy szerveroldali logikát annak biztosítására, hogy az időkorlátok programozottan vezérelhetők vagy kiterjeszthetők
Jó gyakorlatok
1. Több idő kérésének lehetősége
<div class="timed-form-container">
<h2>Időzített űrlap</h2>
<div class="timer-display">
<span class="timer-icon">⏱️</span>
<span>Hátralévő idő: <strong id="time-display">5:00</strong></span>
</div>
<form id="timed-form">
<div class="form-group">
<label for="name">Teljes név:</label>
<input type="text" id="name" name="name" required>
</div>
<div class="form-group">
<label for="email">E-mail cím:</label>
<input type="email" id="email" name="email" required>
</div>
<div class="form-group">
<label for="message">Üzenet:</label>
<textarea id="message" name="message" rows="4" required></textarea>
</div>
<div class="form-actions">
<button type="submit" class="submit-btn">Küldés</button>
<button type="button" onclick="extendTime()" class="extend-btn">
➕ Több idő kérése (+2 perc)
</button>
</div>
</form>
<div id="warning-modal" class="modal" hidden>
<div class="modal-content">
<h3>⚠️ Figyelmeztetés</h3>
<p>Az idő hamarosan lejár! Csak <strong id="warning-time">30</strong> másodperc maradt.</p>
<button onclick="extendTime()" class="modal-btn primary">Több idő kérése</button>
<button onclick="closeWarning()" class="modal-btn secondary">Folytatás</button>
</div>
</div>
</div>
<style>
.timed-form-container {
max-width: 600px;
margin: 20px auto;
padding: 20px;
border: 1px solid #ddd;
border-radius: 8px;
background-color: #f8f9fa;
}
.timer-display {
background-color: #e7f3ff;
padding: 15px;
border-radius: 4px;
margin-bottom: 20px;
text-align: center;
font-size: 18px;
}
.timer-icon {
font-size: 24px;
margin-right: 10px;
}
#time-display {
color: #0056b3;
font-family: monospace;
font-size: 20px;
}
#time-display.warning {
color: #ff6b6b;
animation: pulse 1s infinite;
}
@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.6; }
100% { opacity: 1; }
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 600;
}
.form-group input,
.form-group textarea {
width: 100%;
padding: 10px;
border: 1px solid #ced4da;
border-radius: 4px;
font-size: 16px;
box-sizing: border-box;
}
.form-group input:focus,
.form-group textarea:focus {
outline: 2px solid #007bff;
outline-offset: 2px;
border-color: #007bff;
}
.form-actions {
display: flex;
gap: 10px;
margin-top: 20px;
}
.submit-btn {
flex: 1;
padding: 12px 20px;
background-color: #007bff;
color: white;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
}
.submit-btn:hover {
background-color: #0056b3;
}
.extend-btn {
flex: 1;
padding: 12px 20px;
background-color: #28a745;
color: white;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
}
.extend-btn:hover {
background-color: #218838;
}
.extend-btn:focus,
.submit-btn:focus {
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: 400px;
text-align: center;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
}
.modal-content h3 {
margin-top: 0;
color: #ff6b6b;
}
.modal-btn {
padding: 10px 20px;
margin: 5px;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
}
.modal-btn.primary {
background-color: #28a745;
color: white;
}
.modal-btn.secondary {
background-color: #6c757d;
color: white;
}
.modal-btn:hover {
opacity: 0.9;
}
.modal-btn:focus {
outline: 2px solid #007bff;
outline-offset: 2px;
}
</style>
<script>
let timeLeft = 300; // 5 perc másodpercben
let timer;
let warningShown = false;
let extensionCount = 0;
const maxExtensions = 3;
function startTimer() {
timer = setInterval(() => {
timeLeft--;
updateDisplay();
// Figyelmeztetés megjelenítése 30 másodpercnél
if (timeLeft === 30 && !warningShown) {
showWarning();
warningShown = true;
}
// Idő lejárt
if (timeLeft <= 0) {
clearInterval(timer);
handleTimeout();
}
}, 1000);
}
function updateDisplay() {
const minutes = Math.floor(timeLeft / 60);
const seconds = timeLeft % 60;
const display = `${minutes}:${seconds.toString().padStart(2, '0')}`;
const timeDisplay = document.getElementById('time-display');
timeDisplay.textContent = display;
// Vizuális figyelmeztetés
if (timeLeft <= 30) {
timeDisplay.classList.add('warning');
} else {
timeDisplay.classList.remove('warning');
}
}
function extendTime() {
if (extensionCount >= maxExtensions) {
alert(`Maximum ${maxExtensions} alkalommal kérhetsz időhosszabbítást.`);
return;
}
timeLeft += 120; // +2 perc
extensionCount++;
warningShown = false;
closeWarning();
updateDisplay();
// Visszajelzés a felhasználónak
showNotification(`Idő meghosszabbítva! (+2 perc) Fennmaradó hosszabbítások: ${maxExtensions - extensionCount}`);
// ARIA live region frissítése
const announcement = document.createElement('div');
announcement.setAttribute('role', 'status');
announcement.setAttribute('aria-live', 'polite');
announcement.textContent = `Idő meghosszabbítva. Új hátralévő idő: ${Math.floor(timeLeft / 60)} perc ${timeLeft % 60} másodperc`;
document.body.appendChild(announcement);
setTimeout(() => announcement.remove(), 3000);
}
function showWarning() {
const modal = document.getElementById('warning-modal');
modal.hidden = false;
// Fókusz a figyelmeztetésre
const primaryBtn = modal.querySelector('.modal-btn.primary');
primaryBtn.focus();
// Figyelmeztetés időzítő
let warningTime = 30;
const warningInterval = setInterval(() => {
warningTime--;
document.getElementById('warning-time').textContent = warningTime;
if (warningTime <= 0 || modal.hidden) {
clearInterval(warningInterval);
}
}, 1000);
}
function closeWarning() {
document.getElementById('warning-modal').hidden = true;
}
function handleTimeout() {
alert('Az idő lejárt! Az űrlap adatai mentésre kerültek piszkozatként.');
// Itt lehetne menteni az űrlap adatait localStorage-ba
saveFormData();
// Űrlap letiltása
const form = document.getElementById('timed-form');
const inputs = form.querySelectorAll('input, textarea, button');
inputs.forEach(input => input.disabled = true);
// Időzítő megjelenítés frissítése
document.getElementById('time-display').textContent = 'Lejárt';
}
function saveFormData() {
const formData = {
name: document.getElementById('name').value,
email: document.getElementById('email').value,
message: document.getElementById('message').value,
timestamp: new Date().toISOString()
};
localStorage.setItem('timedFormDraft', JSON.stringify(formData));
console.log('Űrlap adatok mentve:', formData);
}
function showNotification(message) {
const notification = document.createElement('div');
notification.textContent = message;
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background-color: #28a745;
color: white;
padding: 15px 20px;
border-radius: 4px;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
z-index: 1001;
`;
document.body.appendChild(notification);
setTimeout(() => notification.remove(), 4000);
}
// Űrlap beküldés kezelése
document.getElementById('timed-form').addEventListener('submit', (e) => {
e.preventDefault();
clearInterval(timer);
alert('Űrlap sikeresen beküldve!');
// Itt lenne a tényleges beküldés
});
// Időzítő indítása az oldal betöltésekor
window.addEventListener('load', () => {
startTimer();
updateDisplay();
});
</script>
Magyarázat: Ez a példa egy gombot mutat, amely lehetővé teszi a felhasználók számára az időkorlát meghosszabbítását, kontrollt adva nekik az időzítés felett. A rendszer figyelmeztetést is küld, mielőtt az idő lejárna.
2. Időkorlátok letiltásának lehetősége
<div class="session-timeout-container">
<h2>Munkamenet beállítások</h2>
<div class="session-info">
<p>A munkamenet lejár: <span id="countdown" class="countdown-display">10:00</span></p>
<div class="timeout-controls">
<button type="button" onclick="pauseTimeout()" id="pause-btn" class="control-btn pause">
⏸️ Szüneteltetés
</button>
<button type="button" onclick="resetTimeout()" class="control-btn reset">
🔄 Újraindítás
</button>
<button type="button" onclick="disableTimeout()" class="control-btn disable">
❌ Időkorlát kikapcsolása
</button>
</div>
</div>
<div class="session-settings">
<h3>Időkorlát beállítások</h3>
<label class="setting-item">
<input type="checkbox" id="auto-extend" onchange="toggleAutoExtend()">
<span>Automatikus hosszabbítás aktivitás esetén</span>
</label>
<label class="setting-item">
<input type="checkbox" id="sound-alert" checked>
<span>Hangjelzés figyelmeztetéskor</span>
</label>
<div class="setting-item">
<label for="timeout-duration">Időkorlát hossza (perc):</label>
<input type="number" id="timeout-duration" min="1" max="60" value="10"
onchange="updateTimeoutDuration()">
</div>
</div>
<div id="activity-log" class="activity-log">
<h3>Tevékenység napló</h3>
<ul id="log-list"></ul>
</div>
</div>
<style>
.session-timeout-container {
max-width: 600px;
margin: 20px auto;
font-family: Arial, sans-serif;
}
.session-info {
background-color: #f8f9fa;
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
text-align: center;
}
.countdown-display {
font-size: 36px;
font-weight: bold;
color: #007bff;
font-family: monospace;
display: inline-block;
min-width: 120px;
}
.countdown-display.warning {
color: #ffc107;
}
.countdown-display.critical {
color: #dc3545;
animation: blink 1s infinite;
}
.countdown-display.disabled {
color: #6c757d;
text-decoration: line-through;
}
@keyframes blink {
0%, 50% { opacity: 1; }
51%, 100% { opacity: 0.5; }
}
.timeout-controls {
display: flex;
gap: 10px;
justify-content: center;
margin-top: 15px;
flex-wrap: wrap;
}
.control-btn {
padding: 10px 20px;
border: none;
border-radius: 4px;
font-size: 14px;
cursor: pointer;
transition: transform 0.1s;
}
.control-btn:hover {
transform: translateY(-2px);
}
.control-btn:focus {
outline: 2px solid #007bff;
outline-offset: 2px;
}
.control-btn.pause {
background-color: #ffc107;
color: #000;
}
.control-btn.reset {
background-color: #17a2b8;
color: white;
}
.control-btn.disable {
background-color: #dc3545;
color: white;
}
.control-btn:disabled {
background-color: #6c757d;
cursor: not-allowed;
opacity: 0.6;
}
.session-settings {
background-color: #e9ecef;
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
}
.session-settings h3 {
margin-top: 0;
}
.setting-item {
display: block;
margin-bottom: 15px;
cursor: pointer;
}
.setting-item input[type="checkbox"] {
margin-right: 10px;
width: 18px;
height: 18px;
cursor: pointer;
}
.setting-item input[type="number"] {
width: 60px;
padding: 5px;
margin-left: 10px;
border: 1px solid #ced4da;
border-radius: 4px;
}
.activity-log {
background-color: #f1f3f4;
padding: 15px;
border-radius: 8px;
max-height: 200px;
overflow-y: auto;
}
.activity-log h3 {
margin-top: 0;
font-size: 16px;
}
#log-list {
list-style: none;
padding: 0;
margin: 0;
font-size: 14px;
}
#log-list li {
padding: 5px 0;
border-bottom: 1px solid #dee2e6;
}
#log-list li:last-child {
border-bottom: none;
}
.timestamp {
color: #6c757d;
font-size: 12px;
margin-right: 8px;
}
</style>
<script>
let countdown = 600; // 10 perc másodpercben
let interval;
let isPaused = false;
let isDisabled = false;
let autoExtend = false;
let lastActivity = Date.now();
function startCountdown() {
interval = setInterval(() => {
if (!isPaused && !isDisabled) {
countdown--;
updateCountdownDisplay();
// Automatikus hosszabbítás ellenőrzése
if (autoExtend && Date.now() - lastActivity < 30000) {
countdown = Math.max(countdown, 60); // Minimum 1 perc
}
// Figyelmeztetések
if (countdown === 60) {
showAlert('A munkamenet 1 perc múlva lejár!');
} else if (countdown === 30) {
showAlert('A munkamenet 30 másodperc múlva lejár!', true);
} else if (countdown <= 0) {
clearInterval(interval);
handleSessionExpired();
}
}
}, 1000);
}
function updateCountdownDisplay() {
const display = document.getElementById('countdown');
const minutes = Math.floor(countdown / 60);
const seconds = countdown % 60;
display.textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`;
// Vizuális jelzések
display.classList.remove('warning', 'critical', 'disabled');
if (isDisabled) {
display.classList.add('disabled');
display.textContent = 'Kikapcsolva';
} else if (countdown <= 30) {
display.classList.add('critical');
} else if (countdown <= 60) {
display.classList.add('warning');
}
}
function pauseTimeout() {
isPaused = !isPaused;
const pauseBtn = document.getElementById('pause-btn');
if (isPaused) {
pauseBtn.innerHTML = '▶️ Folytatás';
pauseBtn.classList.add('paused');
addLogEntry('Időkorlát szüneteltetve');
} else {
pauseBtn.innerHTML = '⏸️ Szüneteltetés';
pauseBtn.classList.remove('paused');
addLogEntry('Időkorlát folytatva');
}
}
function resetTimeout() {
const duration = parseInt(document.getElementById('timeout-duration').value) * 60;
countdown = duration;
isPaused = false;
isDisabled = false;
updateCountdownDisplay();
addLogEntry('Időkorlát újraindítva');
// Gombok állapotának visszaállítása
document.getElementById('pause-btn').innerHTML = '⏸️ Szüneteltetés';
document.getElementById('pause-btn').disabled = false;
}
function disableTimeout() {
isDisabled = true;
clearInterval(interval);
updateCountdownDisplay();
addLogEntry('Időkorlát kikapcsolva');
// Gombok letiltása
document.getElementById('pause-btn').disabled = true;
document.getElementById('pause-btn').innerHTML = '⏸️ Szüneteltetés';
// Értesítés
showNotification('Időkorlát sikeresen kikapcsolva. A munkamenet nem fog automatikusan lejárni.');
}
function toggleAutoExtend() {
autoExtend = document.getElementById('auto-extend').checked;
addLogEntry(autoExtend ? 'Automatikus hosszabbítás bekapcsolva' : 'Automatikus hosszabbítás kikapcsolva');
}
function updateTimeoutDuration() {
const newDuration = parseInt(document.getElementById('timeout-duration').value);
if (newDuration >= 1 && newDuration <= 60) {
addLogEntry(`Időkorlát módosítva: ${newDuration} perc`);
if (!isDisabled) {
countdown = newDuration * 60;
updateCountdownDisplay();
}
}
}
function showAlert(message, critical = false) {
const soundEnabled = document.getElementById('sound-alert').checked;
// Vizuális figyelmeztetés
const alert = document.createElement('div');
alert.textContent = message;
alert.style.cssText = `
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: ${critical ? '#dc3545' : '#ffc107'};
color: ${critical ? 'white' : 'black'};
padding: 20px 30px;
border-radius: 8px;
font-size: 18px;
box-shadow: 0 4px 20px rgba(0,0,0,0.3);
z-index: 1002;
`;
document.body.appendChild(alert);
// Hangjelzés
if (soundEnabled) {
playSound(critical ? 880 : 440); // Magasabb hang kritikus figyelmeztetéshez
}
setTimeout(() => alert.remove(), 3000);
}
function playSound(frequency) {
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.frequency.value = frequency;
oscillator.type = 'sine';
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5);
oscillator.start(audioContext.currentTime);
oscillator.stop(audioContext.currentTime + 0.5);
}
function handleSessionExpired() {
addLogEntry('Munkamenet lejárt');
alert('A munkamenet lejárt. Kérlek jelentkezz be újra.');
// Itt lenne a kijelentkezés logika
}
function addLogEntry(message) {
const logList = document.getElementById('log-list');
const timestamp = new Date().toLocaleTimeString('hu-HU');
const entry = document.createElement('li');
entry.innerHTML = `<span class="timestamp">${timestamp}</span> ${message}`;
logList.insertBefore(entry, logList.firstChild);
// Maximum 10 bejegyzés megőrzése
while (logList.children.length > 10) {
logList.removeChild(logList.lastChild);
}
}
function showNotification(message) {
const notification = document.createElement('div');
notification.setAttribute('role', 'status');
notification.setAttribute('aria-live', 'polite');
notification.textContent = message;
notification.style.cssText = `
position: fixed;
bottom: 20px;
right: 20px;
background-color: #28a745;
color: white;
padding: 15px 20px;
border-radius: 4px;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
z-index: 1001;
`;
document.body.appendChild(notification);
setTimeout(() => notification.remove(), 5000);
}
// Aktivitás követése
document.addEventListener('click', () => {
lastActivity = Date.now();
});
document.addEventListener('keypress', () => {
lastActivity = Date.now();
});
// Kezdeti indítás
window.addEventListener('load', () => {
startCountdown();
updateCountdownDisplay();
addLogEntry('Munkamenet elindítva');
});
</script>
Magyarázat: A felhasználók teljesen kikapcsolhatják az időkorlátot, megakadályozva a kényszerített időzítési korlátozásokat. További opciók közé tartozik a szüneteltetés, újraindítás és az automatikus hosszabbítás.
3. Figyelmeztetés biztosítása időtúllépés előtt
<div class="quiz-container">
<h2>Online kvíz - Akadálymentesség alapjai</h2>
<div class="quiz-header">
<div class="quiz-info">
<span>Kérdés: <strong id="current-question">1</strong> / 5</span>
<span>Idő: <strong id="quiz-timer">15:00</strong></span>
</div>
<div class="progress-bar">
<div id="time-progress" class="progress-fill"></div>
</div>
</div>
<div id="question-area" class="question-area">
<h3>Mi a WCAG rövidítés jelentése?</h3>
<div class="options">
<label class="option">
<input type="radio" name="answer" value="a">
<span>Web Content Accessibility Guidelines</span>
</label>
<label class="option">
<input type="radio" name="answer" value="b">
<span>World Computer Access Group</span>
</label>
<label class="option">
<input type="radio" name="answer" value="c">
<span>Website Creation and Guidelines</span>
</label>
</div>
</div>
<div class="quiz-actions">
<button onclick="previousQuestion()" id="prev-btn" disabled>Előző</button>
<button onclick="nextQuestion()" id="next-btn">Következő</button>
<button onclick="submitQuiz()" id="submit-btn" style="display:none;">Beküldés</button>
</div>
<!-- Időfigyelmeztetés modal -->
<div id="time-warning-modal" class="warning-modal" hidden>
<div class="warning-content">
<h3>⏰ Időfigyelmeztetés</h3>
<p>A kvíz <strong id="warning-minutes">5</strong> perc múlva lejár!</p>
<p>Szeretnéd:</p>
<div class="warning-actions">
<button onclick="continueQuiz()" class="btn-continue">Folytatás</button>
<button onclick="requestMoreTime()" class="btn-extend">Több idő kérése (+10 perc)</button>
<button onclick="saveAndExit()" class="btn-save">Mentés és kilépés</button>
</div>
</div>
</div>
</div>
<style>
.quiz-container {
max-width: 700px;
margin: 20px auto;
padding: 20px;
background-color: #f8f9fa;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.quiz-header {
margin-bottom: 20px;
}
.quiz-info {
display: flex;
justify-content: space-between;
margin-bottom: 10px;
font-size: 16px;
}
#quiz-timer {
color: #007bff;
font-family: monospace;
font-size: 18px;
}
#quiz-timer.warning {
color: #ffc107;
}
#quiz-timer.critical {
color: #dc3545;
animation: timer-pulse 1s infinite;
}
@keyframes timer-pulse {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.1); }
}
.progress-bar {
width: 100%;
height: 8px;
background-color: #e9ecef;
border-radius: 4px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background-color: #28a745;
transition: width 1s linear, background-color 0.3s;
}
.progress-fill.warning {
background-color: #ffc107;
}
.progress-fill.critical {
background-color: #dc3545;
}
.question-area {
background-color: white;
padding: 30px;
border-radius: 8px;
margin-bottom: 20px;
}
.question-area h3 {
margin-top: 0;
margin-bottom: 20px;
}
.options {
display: flex;
flex-direction: column;
gap: 15px;
}
.option {
display: flex;
align-items: center;
padding: 15px;
background-color: #f8f9fa;
border: 2px solid transparent;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
}
.option:hover {
background-color: #e9ecef;
border-color: #ced4da;
}
.option input[type="radio"] {
margin-right: 10px;
width: 18px;
height: 18px;
cursor: pointer;
}
.option input[type="radio"]:focus {
outline: 2px solid #007bff;
outline-offset: 2px;
}
.quiz-actions {
display: flex;
justify-content: space-between;
gap: 10px;
}
.quiz-actions button {
padding: 10px 20px;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
transition: opacity 0.2s;
}
.quiz-actions button:focus {
outline: 2px solid #007bff;
outline-offset: 2px;
}
#prev-btn, #next-btn {
background-color: #007bff;
color: white;
}
#prev-btn:hover:not(:disabled), #next-btn:hover {
background-color: #0056b3;
}
#prev-btn:disabled {
background-color: #6c757d;
cursor: not-allowed;
opacity: 0.6;
}
#submit-btn {
background-color: #28a745;
color: white;
}
#submit-btn:hover {
background-color: #218838;
}
.warning-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
.warning-modal[hidden] {
display: none;
}
.warning-content {
background-color: white;
padding: 30px;
border-radius: 8px;
max-width: 450px;
text-align: center;
box-shadow: 0 4px 20px rgba(0,0,0,0.3);
}
.warning-content h3 {
color: #ffc107;
margin-top: 0;
}
.warning-actions {
display: flex;
flex-direction: column;
gap: 10px;
margin-top: 20px;
}
.warning-actions button {
padding: 12px 20px;
border: none;
border-radius: 4px;
font-size: 16px;
cursor: pointer;
}
.btn-continue {
background-color: #007bff;
color: white;
}
.btn-extend {
background-color: #28a745;
color: white;
}
.btn-save {
background-color: #6c757d;
color: white;
}
.warning-actions button:hover {
opacity: 0.9;
}
.warning-actions button:focus {
outline: 2px solid #000;
outline-offset: 2px;
}
</style>
<script>
let quizTime = 900; // 15 perc
let quizTimer;
let warningIntervals = [300, 60, 30]; // 5 perc, 1 perc, 30 mp
let warningsShown = new Set();
let currentQuestionIndex = 0;
let answers = {};
const questions = [
{
question: "Mi a WCAG rövidítés jelentése?",
options: [
"Web Content Accessibility Guidelines",
"World Computer Access Group",
"Website Creation and Guidelines"
],
correct: 0
},
{
question: "Melyik az ajánlott minimális kontraszt arány normál szöveghez?",
options: ["3:1", "4.5:1", "7:1"],
correct: 1
},
// További kérdések...
];
function startQuizTimer() {
updateTimerDisplay();
updateProgressBar();
quizTimer = setInterval(() => {
quizTime--;
updateTimerDisplay();
updateProgressBar();
checkWarnings();
if (quizTime <= 0) {
clearInterval(quizTimer);
handleQuizTimeout();
}
}, 1000);
}
function updateTimerDisplay() {
const minutes = Math.floor(quizTime / 60);
const seconds = quizTime % 60;
const display = document.getElementById('quiz-timer');
display.textContent = `${minutes}:${seconds.toString().padStart(2, '0')}`;
// Vizuális jelzések
display.classList.remove('warning', 'critical');
if (quizTime <= 30) {
display.classList.add('critical');
} else if (quizTime <= 300) {
display.classList.add('warning');
}
}
function updateProgressBar() {
const totalTime = 900;
const progress = (quizTime / totalTime) * 100;
const progressBar = document.getElementById('time-progress');
progressBar.style.width = progress + '%';
progressBar.classList.remove('warning', 'critical');
if (quizTime <= 30) {
progressBar.classList.add('critical');
} else if (quizTime <= 300) {
progressBar.classList.add('warning');
}
}
function checkWarnings() {
warningIntervals.forEach(interval => {
if (quizTime === interval && !warningsShown.has(interval)) {
warningsShown.add(interval);
showTimeWarning(interval);
}
});
}
function showTimeWarning(timeLeft) {
const modal = document.getElementById('time-warning-modal');
const warningMinutes = Math.floor(timeLeft / 60);
document.getElementById('warning-minutes').textContent =
timeLeft >= 60 ? warningMinutes : `${timeLeft} másodperc`;
modal.hidden = false;
// Fókusz a folytatás gombra
setTimeout(() => {
document.querySelector('.btn-continue').focus();
}, 100);
// Hangjelzés
playWarningSound();
// Képernyőolvasó értesítés
announceToScreenReader(`Figyelmeztetés: A kvíz ${warningMinutes} perc múlva lejár.`);
}
function continueQuiz() {
document.getElementById('time-warning-modal').hidden = true;
}
function requestMoreTime() {
quizTime += 600; // +10 perc
document.getElementById('time-warning-modal').hidden = true;
showNotification('10 perc hozzáadva a kvíz idejéhez');
announceToScreenReader('10 perccel meghosszabbítva a kvíz ideje');
}
function saveAndExit() {
saveQuizProgress();
clearInterval(quizTimer);
alert('A kvíz állapota mentve. Később folytathatod.');
// Itt lenne a mentés és kilépés logika
}
function saveQuizProgress() {
const quizState = {
currentQuestion: currentQuestionIndex,
answers: answers,
timeRemaining: quizTime,
timestamp: new Date().toISOString()
};
localStorage.setItem('quizProgress', JSON.stringify(quizState));
}
function handleQuizTimeout() {
alert('Az idő lejárt! A válaszaid automatikusan beküldésre kerültek.');
submitQuiz();
}
function playWarningSound() {
// Egyszerű hangjelzés
const audio = new Audio('data:audio/wav;base64,UklGRnoGAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQoGAACBhYqFbF1fdJivrJBhNjVgodDbq2EcBj+a2/LDciUFLIHO8tiJNwgZaLvt559NEAxQp+PwtmMcBjiR1/LMeSwFJHfH8N2QQAoUXrTp66hVFApGn+DyvmwhBTGH0fPTgjMGHm7A7+OZURE');
audio.play().catch(e => console.log('Hangjelzés nem játszható le'));
}
function announceToScreenReader(message) {
const announcement = document.createElement('div');
announcement.setAttribute('role', 'alert');
announcement.setAttribute('aria-live', 'assertive');
announcement.className = 'sr-only';
announcement.textContent = message;
document.body.appendChild(announcement);
setTimeout(() => announcement.remove(), 3000);
}
function showNotification(message) {
const notification = document.createElement('div');
notification.textContent = message;
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background-color: #28a745;
color: white;
padding: 15px 20px;
border-radius: 4px;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
z-index: 1001;
`;
document.body.appendChild(notification);
setTimeout(() => notification.remove(), 4000);
}
// Kvíz navigáció (egyszerűsített)
function nextQuestion() {
// Válasz mentése
const selectedOption = document.querySelector('input[name="answer"]:checked');
if (selectedOption) {
answers[currentQuestionIndex] = selectedOption.value;
}
currentQuestionIndex++;
if (currentQuestionIndex >= questions.length - 1) {
document.getElementById('next-btn').style.display = 'none';
document.getElementById('submit-btn').style.display = 'block';
}
document.getElementById('prev-btn').disabled = false;
// Következő kérdés betöltése...
document.getElementById('current-question').textContent = currentQuestionIndex + 1;
}
function previousQuestion() {
currentQuestionIndex--;
if (currentQuestionIndex <= 0) {
document.getElementById('prev-btn').disabled = true;
}
document.getElementById('next-btn').style.display = 'block';
document.getElementById('submit-btn').style.display = 'none';
document.getElementById('current-question').textContent = currentQuestionIndex + 1;
}
function submitQuiz() {
clearInterval(quizTimer);
saveQuizProgress();
alert('Kvíz beküldve! Köszönjük a részvételt.');
}
// Stílusok a képernyőolvasóknak
const style = document.createElement('style');
style.textContent = `
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
`;
document.head.appendChild(style);
// Kvíz indítása
window.addEventListener('load', () => {
startQuizTimer();
});
</script>
Magyarázat: A felhasználók időben figyelmeztetést kapnak, lehetőséget adva nekik a felkészülésre vagy a munkamenet meghosszabbítására. Többszintű figyelmeztetések és választási lehetőségek biztosítottak.
Rossz gyakorlatok
Kemény időkorlátok kiterjesztés vagy figyelmeztetés nélkül
<script>
// ROSSZ: Nincs mód az időkorlát kiterjesztésére
setTimeout(() => {
alert('Az idő lejárt! Ki leszel jelentkeztetve.');
// Automatikus kijelentkeztetés
window.location.href = '/logout';
}, 60000); // 1 perc után
// ROSSZ: Űrlap automatikus beküldése figyelmeztetés nélkül
setTimeout(() => {
document.getElementById('form').submit();
// Felhasználó elveszítheti az adatokat
}, 300000); // 5 perc
</script>
Probléma: A felhasználóknak nincs módjuk kiterjeszteni vagy letiltani az időkorlátot, ami frusztrációt vagy adatvesztést okozhat.
Láthatatlan vagy hozzáférhetetlen idővezérlők
<!-- ROSSZ: Az időkiterjesztés csak vizuálisan látható -->
<div class="timeout-warning" style="position: absolute; top: -9999px;">
Az idő hamarosan lejár!
<!-- A gomb csak egérrel kattintható -->
<span onclick="extendTime()" style="cursor: pointer; color: blue;">
Kattints ide több időért
</span>
</div>
<!-- ROSSZ: Időkijelző képként -->
<img src="timer-display.png" alt="">
<!-- Képernyőolvasók nem tudják felolvasni az időt -->
Probléma: Ha az idő kiterjesztésének lehetősége csak vizuálisan elérhető vagy egérinterakcióval, a billentyűzet és képernyőolvasó felhasználók nem férhetnek hozzá.
Rövid időkorlátok komplex feladatokra
<script>
// ROSSZ: 30 másodperc egy hosszú űrlap kitöltésére
let formTimeout = setTimeout(() => {
document.getElementById('complex-form').reset();
alert('Időtúllépés! Az űrlap törlésre került.');
}, 30000);
// ROSSZ: 1 perc egy 50 kérdéses teszt megoldására
startTest({
questions: 50,
timeLimit: 60, // másodperc
noExtension: true
});
</script>
Probléma: Rövid időkorlátok alkalmazása űrlapokon vagy teszteken a felhasználói igények figyelembevétele nélkül akadálymentességi akadályokat teremt.
Időkorlát figyelmeztetés nélküli újraindítása
<script>
// ROSSZ: Időzítő újraindul minden interakciónál értesítés nélkül
let inactivityTimer;
function resetTimer() {
clearTimeout(inactivityTimer);
inactivityTimer = setTimeout(() => {
// Hirtelen kijelentkeztetés
window.location.href = '/logout';
}, 120000); // 2 perc
}
// Minden kattintás újraindítja
document.addEventListener('click', resetTimer);
document.addEventListener('keypress', resetTimer);
// Felhasználó nem tudja, mennyi ideje van
</script>
Probléma: A felhasználók nem tudják, hogy az időzítő újraindul, és nem látják, mennyi idejük van hátra.
Nem megszakítható automatikus frissítés
<!-- ROSSZ: Automatikus újratöltés vezérlés nélkül -->
<meta http-equiv="refresh" content="60">
<script>
// ROSSZ: JavaScript alapú frissítés letiltási lehetőség nélkül
setInterval(() => {
location.reload();
// Elveszhetnek a nem mentett adatok
}, 60000);
// ROSSZ: AJAX frissítés figyelmeztetés nélkül
setInterval(() => {
fetch('/update-content')
.then(response => response.text())
.then(html => {
document.body.innerHTML = html;
// Fókusz és állapot elvész
});
}, 30000);
</script>
Probléma: Az automatikus frissítések megszakítják a felhasználó munkáját, elveszítik a fókuszt és az űrlap adatokat, különösen problémás a képernyőolvasó felhasználók számára.
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!