2.2.2 - Szüneteltetés, leállítás, elrejtés

Röviden a szabványpontról

A WCAG 2.2.2 (Pause, Stop, Hide) előírja, hogy minden automatikusan mozgó, villogó, gördülő vagy automatikusan frissülő információ esetén a felhasználóknak lehetőséget kell biztosítani a tartalom szüneteltetésére, leállítására vagy elrejtésére. Ez vonatkozik minden olyan tartalomra, amely automatikusan indul, öt másodpercnél hosszabb ideig tart, és más tartalommal párhuzamosan jelenik meg. Ezen felül az automatikusan frissülő információk esetében a felhasználóknak képesnek kell lenniük a frissítés szüneteltetésére, leállítására, elrejtésére vagy a frissítési gyakoriság szabályozására.

Kiket érint

Elsődleges felhasználók: Figyelemzavaros vagy kognitív fogyatékossággal élő emberek, akiket a mozgó vagy villogó tartalom megzavarhat. Epilepsziával élők, akiknél a villogó tartalom rohamot válthat ki. Képernyőolvasót használó felhasználók, akiknél az automatikusan frissülő tartalom zavarja a navigációt.

Másodlagos előnyök: Minden felhasználó, aki zavarónak találja a mozgó tartalmat vagy szeretné kontrollálni az automatikus frissítéseket. Lassú internetkapcsolattal rendelkező felhasználók, akik számára az automatikus frissítések problémát okozhatnak.

Tesztelés

  1. Automatikus tartalom azonosítása: Keresd meg az összes automatikusan mozgó, villogó, gördülő vagy frissülő tartalmat az oldalon
  2. Időtartam ellenőrzése: Mérd meg, hogy az automatikus mozgás vagy animáció 5 másodpercnél hosszabb ideig tart-e
  3. Vezérlők tesztelése: Ellenőrizd, hogy van-e szüneteltetés, leállítás vagy elrejtés lehetőség
  4. Billentyűzetes hozzáférés: Győződj meg róla, hogy a vezérlők billentyűzettel is elérhetők
  5. Képernyőolvasó kompatibilitás: Teszteld, hogy a vezérlők megfelelően működnek-e képernyőolvasóval

Jó gyakorlatok

1. Carousel szüneteltethető vezérlőkkel

<div class="carousel-container" role="region" aria-label="Képváltó" aria-roledescription="carousel">
  <div class="carousel-controls">
    <button id="carousel-play-pause" aria-label="Carousel szüneteltetése">
      <span class="pause-icon" aria-hidden="true">⏸️</span>
      <span class="play-icon" hidden aria-hidden="true">▶️</span>
    </button>
  </div>
  
  <div class="carousel-wrapper">
    <div class="carousel-items" id="carousel">
      <div class="carousel-item active" aria-label="1. dia 5-ből">
        <img src="slide1.jpg" alt="Első dia leírása">
        <div class="carousel-caption">
          <h3>Első dia címe</h3>
          <p>Első dia tartalma</p>
        </div>
      </div>
      <div class="carousel-item" aria-label="2. dia 5-ből">
        <img src="slide2.jpg" alt="Második dia leírása">
        <div class="carousel-caption">
          <h3>Második dia címe</h3>
          <p>Második dia tartalma</p>
        </div>
      </div>
      <!-- További diák... -->
    </div>
    
    <div class="carousel-nav">
      <button class="carousel-prev" aria-label="Előző dia">❮</button>
      <button class="carousel-next" aria-label="Következő dia">❯</button>
    </div>
  </div>
  
  <div class="carousel-indicators" role="tablist">
    <button role="tab" aria-selected="true" aria-label="Ugrás az 1. diára"></button>
    <button role="tab" aria-selected="false" aria-label="Ugrás a 2. diára"></button>
    <!-- További indikátorok... -->
  </div>
</div>

<style>
.carousel-container {
  position: relative;
  max-width: 800px;
  margin: 20px auto;
}

.carousel-controls {
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 10;
}

#carousel-play-pause {
  background: rgba(0, 0, 0, 0.7);
  color: white;
  border: none;
  padding: 10px;
  border-radius: 4px;
  cursor: pointer;
  font-size: 20px;
}

#carousel-play-pause:hover,
#carousel-play-pause:focus {
  background: rgba(0, 0, 0, 0.9);
  outline: 2px solid #4CAF50;
  outline-offset: 2px;
}

.carousel-wrapper {
  position: relative;
  overflow: hidden;
}

.carousel-items {
  display: flex;
  transition: transform 0.5s ease;
}

.carousel-item {
  min-width: 100%;
  position: relative;
}

.carousel-item img {
  width: 100%;
  height: auto;
}

.carousel-caption {
  position: absolute;
  bottom: 20px;
  left: 20px;
  right: 20px;
  background: rgba(0, 0, 0, 0.8);
  color: white;
  padding: 15px;
  border-radius: 4px;
}

.carousel-nav button {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  background: rgba(0, 0, 0, 0.5);
  color: white;
  border: none;
  padding: 20px 15px;
  cursor: pointer;
  font-size: 24px;
}

.carousel-prev {
  left: 10px;
}

.carousel-next {
  right: 10px;
}

.carousel-nav button:hover,
.carousel-nav button:focus {
  background: rgba(0, 0, 0, 0.8);
  outline: 2px solid white;
}

.carousel-indicators {
  display: flex;
  justify-content: center;
  gap: 10px;
  margin-top: 15px;
}

.carousel-indicators button {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  border: 2px solid #333;
  background: transparent;
  cursor: pointer;
}

.carousel-indicators button[aria-selected="true"] {
  background: #333;
}
</style>

<script>
let carouselPlaying = true;
let currentSlide = 0;
const slides = document.querySelectorAll('.carousel-item');
const totalSlides = slides.length;
let autoPlayInterval;

const playPauseBtn = document.getElementById('carousel-play-pause');
const pauseIcon = playPauseBtn.querySelector('.pause-icon');
const playIcon = playPauseBtn.querySelector('.play-icon');

// Automatikus lejátszás funkció
function startAutoPlay() {
  autoPlayInterval = setInterval(() => {
    if (carouselPlaying) {
      nextSlide();
    }
  }, 5000); // 5 másodpercenként vált
}

// Következő diára ugrás
function nextSlide() {
  slides[currentSlide].classList.remove('active');
  currentSlide = (currentSlide + 1) % totalSlides;
  slides[currentSlide].classList.add('active');
  updateCarousel();
  updateIndicators();
}

// Előző diára ugrás
function prevSlide() {
  slides[currentSlide].classList.remove('active');
  currentSlide = (currentSlide - 1 + totalSlides) % totalSlides;
  slides[currentSlide].classList.add('active');
  updateCarousel();
  updateIndicators();
}

// Carousel pozíció frissítése
function updateCarousel() {
  const carousel = document.getElementById('carousel');
  carousel.style.transform = `translateX(-${currentSlide * 100}%)`;
}

// Indikátorok frissítése
function updateIndicators() {
  const indicators = document.querySelectorAll('.carousel-indicators button');
  indicators.forEach((indicator, index) => {
    indicator.setAttribute('aria-selected', index === currentSlide);
  });
}

// Szüneteltetés/folytatás
playPauseBtn.addEventListener('click', () => {
  carouselPlaying = !carouselPlaying;
  
  if (carouselPlaying) {
    pauseIcon.hidden = false;
    playIcon.hidden = true;
    playPauseBtn.setAttribute('aria-label', 'Carousel szüneteltetése');
  } else {
    pauseIcon.hidden = true;
    playIcon.hidden = false;
    playPauseBtn.setAttribute('aria-label', 'Carousel folytatása');
  }
});

// Navigációs gombok
document.querySelector('.carousel-prev').addEventListener('click', () => {
  carouselPlaying = false; // Automatikus lejátszás leállítása kézi navigációnál
  playPauseBtn.click(); // Frissíti a gombot
  prevSlide();
});

document.querySelector('.carousel-next').addEventListener('click', () => {
  carouselPlaying = false;
  playPauseBtn.click();
  nextSlide();
});

// Indikátorok kattintás kezelése
document.querySelectorAll('.carousel-indicators button').forEach((indicator, index) => {
  indicator.addEventListener('click', () => {
    carouselPlaying = false;
    if (!pauseIcon.hidden) playPauseBtn.click();
    slides[currentSlide].classList.remove('active');
    currentSlide = index;
    slides[currentSlide].classList.add('active');
    updateCarousel();
    updateIndicators();
  });
});

// Automatikus lejátszás indítása
startAutoPlay();

// Tisztítás oldal elhagyásakor
window.addEventListener('beforeunload', () => {
  clearInterval(autoPlayInterval);
});
</script>

2. Automatikusan frissülő tartalom vezérlőkkel

<div class="live-feed-container">
  <div class="feed-header">
    <h2>Élő hírek</h2>
    <div class="feed-controls">
      <button id="pause-feed" aria-pressed="false">
        <span class="icon">⏸️</span>
        <span class="text">Szüneteltetés</span>
      </button>
      <label for="refresh-interval">
        Frissítési gyakoriság:
        <select id="refresh-interval">
          <option value="5">5 másodperc</option>
          <option value="10" selected>10 másodperc</option>
          <option value="30">30 másodperc</option>
          <option value="60">1 perc</option>
          <option value="0">Kézi frissítés</option>
        </select>
      </label>
      <button id="refresh-now">Frissítés most</button>
    </div>
  </div>
  
  <div class="feed-content" role="region" aria-live="polite" aria-atomic="false">
    <div class="feed-status" aria-live="polite">
      Utolsó frissítés: <time id="last-update">Most</time>
    </div>
    <ul id="news-list" class="news-items">
      <li class="news-item">
        <time datetime="2024-01-10T10:00:00">10:00</time>
        <p>Első hír tartalma...</p>
      </li>
      <!-- További hírek... -->
    </ul>
  </div>
</div>

<style>
.live-feed-container {
  max-width: 600px;
  margin: 20px auto;
  border: 1px solid #ddd;
  border-radius: 8px;
  overflow: hidden;
}

.feed-header {
  background: #f0f0f0;
  padding: 15px;
  border-bottom: 1px solid #ddd;
}

.feed-header h2 {
  margin: 0 0 10px 0;
}

.feed-controls {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
}

#pause-feed {
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 8px 15px;
  background: #007bff;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

#pause-feed[aria-pressed="true"] {
  background: #dc3545;
}

#pause-feed:hover,
#pause-feed:focus {
  opacity: 0.9;
  outline: 2px solid #333;
  outline-offset: 2px;
}

label {
  display: flex;
  align-items: center;
  gap: 5px;
}

select {
  padding: 5px;
  border: 1px solid #ccc;
  border-radius: 4px;
}

#refresh-now {
  padding: 8px 15px;
  background: #28a745;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

#refresh-now:hover,
#refresh-now:focus {
  opacity: 0.9;
  outline: 2px solid #333;
  outline-offset: 2px;
}

.feed-content {
  padding: 15px;
}

.feed-status {
  font-size: 14px;
  color: #666;
  margin-bottom: 10px;
}

.news-items {
  list-style: none;
  padding: 0;
  margin: 0;
}

.news-item {
  padding: 10px;
  border-bottom: 1px solid #eee;
  animation: fadeIn 0.5s ease-in;
}

.news-item:last-child {
  border-bottom: none;
}

.news-item time {
  font-weight: bold;
  color: #007bff;
  margin-right: 10px;
}

@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(-10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
</style>

<script>
let feedPaused = false;
let refreshInterval = 10000; // 10 másodperc alapértelmezetten
let intervalId;

const pauseBtn = document.getElementById('pause-feed');
const intervalSelect = document.getElementById('refresh-interval');
const refreshBtn = document.getElementById('refresh-now');
const lastUpdateTime = document.getElementById('last-update');
const newsList = document.getElementById('news-list');

// Szüneteltetés/folytatás
pauseBtn.addEventListener('click', () => {
  feedPaused = !feedPaused;
  pauseBtn.setAttribute('aria-pressed', feedPaused);
  
  if (feedPaused) {
    pauseBtn.querySelector('.text').textContent = 'Folytatás';
    clearInterval(intervalId);
  } else {
    pauseBtn.querySelector('.text').textContent = 'Szüneteltetés';
    if (refreshInterval > 0) {
      startAutoRefresh();
    }
  }
});

// Frissítési gyakoriság változtatása
intervalSelect.addEventListener('change', (e) => {
  refreshInterval = parseInt(e.target.value) * 1000;
  clearInterval(intervalId);
  
  if (refreshInterval > 0 && !feedPaused) {
    startAutoRefresh();
  }
});

// Azonnali frissítés
refreshBtn.addEventListener('click', () => {
  fetchNewContent();
});

// Automatikus frissítés indítása
function startAutoRefresh() {
  intervalId = setInterval(() => {
    if (!feedPaused) {
      fetchNewContent();
    }
  }, refreshInterval);
}

// Új tartalom lekérése (szimulált)
function fetchNewContent() {
  const now = new Date();
  const timeString = now.toLocaleTimeString('hu-HU', { 
    hour: '2-digit', 
    minute: '2-digit',
    second: '2-digit'
  });
  
  // Frissítési idő frissítése
  lastUpdateTime.textContent = timeString;
  lastUpdateTime.setAttribute('datetime', now.toISOString());
  
  // Új hír hozzáadása (példa)
  const newItem = document.createElement('li');
  newItem.className = 'news-item';
  newItem.innerHTML = `
    <time datetime="${now.toISOString()}">${timeString}</time>
    <p>Új hír érkezett ${timeString} időpontban</p>
  `;
  
  // Beszúrás az elejére
  newsList.insertBefore(newItem, newsList.firstChild);
  
  // Maximum 10 hír megtartása
  while (newsList.children.length > 10) {
    newsList.removeChild(newsList.lastChild);
  }
  
  // Képernyőolvasó értesítése
  const announcement = `Új hír érkezett ${timeString} időpontban`;
  announceToScreenReader(announcement);
}

// Képernyőolvasó értesítése
function announceToScreenReader(message) {
  const announcement = document.createElement('div');
  announcement.setAttribute('role', 'status');
  announcement.setAttribute('aria-live', 'polite');
  announcement.className = 'sr-only';
  announcement.textContent = message;
  document.body.appendChild(announcement);
  
  setTimeout(() => {
    document.body.removeChild(announcement);
  }, 1000);
}

// Automatikus frissítés indítása, ha be van állítva
if (refreshInterval > 0) {
  startAutoRefresh();
}

// Tisztítás oldal elhagyásakor
window.addEventListener('beforeunload', () => {
  clearInterval(intervalId);
});
</script>

3. Animált háttér választható letiltással

<div class="animated-section">
  <div class="animation-control">
    <label class="toggle-switch">
      <input type="checkbox" id="toggle-animation" checked>
      <span class="slider"></span>
      <span class="label-text">Animáció engedélyezése</span>
    </label>
  </div>
  
  <div class="content-with-animation">
    <div class="animated-background" id="animated-bg"></div>
    <div class="content">
      <h2>Tartalom animált háttérrel</h2>
      <p>Ez a tartalom egy opcionális animált háttér előtt jelenik meg. 
      A felhasználók kikapcsolhatják az animációt a fenti kapcsolóval.</p>
    </div>
  </div>
</div>

<style>
.animated-section {
  position: relative;
  min-height: 400px;
  overflow: hidden;
}

.animation-control {
  position: absolute;
  top: 20px;
  right: 20px;
  z-index: 100;
  background: rgba(255, 255, 255, 0.9);
  padding: 10px;
  border-radius: 8px;
  box-shadow: 0 2px 5px rgba(0,0,0,0.2);
}

.toggle-switch {
  position: relative;
  display: flex;
  align-items: center;
  cursor: pointer;
}

.toggle-switch input {
  opacity: 0;
  width: 0;
  height: 0;
}

.slider {
  position: relative;
  width: 50px;
  height: 26px;
  background-color: #ccc;
  border-radius: 34px;
  transition: .4s;
  margin-right: 10px;
}

.slider:before {
  position: absolute;
  content: "";
  height: 18px;
  width: 18px;
  left: 4px;
  bottom: 4px;
  background-color: white;
  border-radius: 50%;
  transition: .4s;
}

input:checked + .slider {
  background-color: #2196F3;
}

input:checked + .slider:before {
  transform: translateX(24px);
}

input:focus + .slider {
  box-shadow: 0 0 1px #2196F3;
  outline: 2px solid #333;
  outline-offset: 2px;
}

.content-with-animation {
  position: relative;
  height: 400px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.animated-background {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: linear-gradient(45deg, #ff6b6b, #4ecdc4, #45b7d1, #96ceb4);
  background-size: 400% 400%;
}

.animated-background.animating {
  animation: gradientShift 10s ease infinite;
}

@keyframes gradientShift {
  0% {
    background-position: 0% 50%;
  }
  50% {
    background-position: 100% 50%;
  }
  100% {
    background-position: 0% 50%;
  }
}

.content {
  position: relative;
  background: rgba(255, 255, 255, 0.95);
  padding: 30px;
  border-radius: 8px;
  max-width: 500px;
  text-align: center;
  box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}

/* Csökkentett mozgás preferencia tiszteletben tartása */
@media (prefers-reduced-motion: reduce) {
  .animated-background.animating {
    animation: none;
  }
  
  .slider,
  .slider:before {
    transition: none;
  }
}
</style>

<script>
const animationToggle = document.getElementById('toggle-animation');
const animatedBg = document.getElementById('animated-bg');

// Kezdeti állapot beállítása
if (animationToggle.checked) {
  animatedBg.classList.add('animating');
}

// Animáció ki/be kapcsolása
animationToggle.addEventListener('change', (e) => {
  if (e.target.checked) {
    animatedBg.classList.add('animating');
    announceToScreenReader('Háttér animáció bekapcsolva');
  } else {
    animatedBg.classList.remove('animating');
    announceToScreenReader('Háttér animáció kikapcsolva');
  }
});

// Csökkentett mozgás preferencia ellenőrzése
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)');

function handleReducedMotionChange() {
  if (prefersReducedMotion.matches) {
    animationToggle.checked = false;
    animatedBg.classList.remove('animating');
    animationToggle.disabled = true;
    document.querySelector('.label-text').textContent = 'Animáció letiltva (rendszer beállítás)';
  } else {
    animationToggle.disabled = false;
    document.querySelector('.label-text').textContent = 'Animáció engedélyezése';
  }
}

// Kezdeti ellenőrzés
handleReducedMotionChange();

// Változások figyelése
prefersReducedMotion.addEventListener('change', handleReducedMotionChange);

// Képernyőolvasó értesítése
function announceToScreenReader(message) {
  const announcement = document.createElement('div');
  announcement.setAttribute('role', 'status');
  announcement.setAttribute('aria-live', 'polite');
  announcement.className = 'sr-only';
  announcement.textContent = message;
  document.body.appendChild(announcement);
  
  setTimeout(() => {
    document.body.removeChild(announcement);
  }, 1000);
}
</script>

Rossz gyakorlatok

Automatikus videó lejátszás vezérlők nélkül

<video autoplay loop muted>
  <source src="background-video.mp4" type="video/mp4">
</video>

Probléma: Nincs lehetőség a videó szüneteltetésére vagy leállítására.

Megállíthatatlan carousel

<div class="carousel">
  <div class="slide">Dia 1</div>
  <div class="slide">Dia 2</div>
</div>

<script>
// Folyamatos automatikus váltás vezérlők nélkül
setInterval(() => {
  // dia váltás logika
}, 3000);
</script>

Probléma: A felhasználóknak nincs módjuk megállítani az automatikus váltást.

Villogó tartalom szabályozás nélkül

<style>
.flashing-text {
  animation: flash 0.5s infinite;
}

@keyframes flash {
  0%, 50% { opacity: 1; }
  51%, 100% { opacity: 0; }
}
</style>

<h1 class="flashing-text">AKCIÓ!</h1>

Probléma: A gyors villogás zavaró és potenciálisan veszélyes lehet.

Automatikus frissítés értesítés nélkül

<script>
// Tartalom frissítése 5 másodpercenként figyelmeztetés nélkül
setInterval(() => {
  fetch('/api/news')
    .then(response => response.json())
    .then(data => {
      document.getElementById('news').innerHTML = data.content;
    });
}, 5000);
</script>

Probléma: A felhasználók nem tudják szabályozni vagy leállítani a frissítéseket.

Végtelen görgetés kontroll nélkül

<script>
window.addEventListener('scroll', () => {
  if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 100) {
    // Automatikusan több tartalom betöltése
    loadMoreContent();
  }
});
</script>

Probléma: A felhasználók nem tudják elérni a láblécet vagy leállítani az automatikus betöltést.

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!

Ez a weboldal sütiket használ a böngészési élmény javítása és a webhely megfelelő működésének biztosítása érdekében. A webhely használatának folytatásával elismeri és elfogadja a sütik használatát.

Összes elfogadása Csak a szükségesek elfogadása