2.5.1 - Mutatógesztusok

Röviden a szabványpontról

A WCAG 2.5.1 (Pointer Gestures) előírja, hogy minden funkcionalitás, amely többpontos vagy útvonal-alapú gesztusokat használ (mint csípés, húzás vagy forgatás), egypontos bevitellel is működtethető legyen (például egyszerű érintés vagy kattintás). Ez azt jelenti, hogy a felhasználókat nem szabad összetett gesztusok használatára kényszeríteni, a tartalom, vagy vezérlők kezeléséhez.

Mire vonatkozik: Minden interaktív elemre vagy funkcionalitásra egy weboldalon vagy alkalmazásban, amely többujjas vagy összetett mozdulatokat igényel.

Cél: Biztosítani, hogy a korlátozott mozgási képességekkel rendelkező vagy segítő technológiákat használó felhasználók:

  • gond nélkül működtethessék az interaktív komponenseket
  • Ne kelljen összetett gesztusokat végrehajtaniuk
  • Alternatív módszerekkel érhessék el ugyanazt a funkcionalitást
  • Egyszerű érintéssel vagy kattintással is használhassák a felületet

Kiket érint

Elsődleges felhasználók: Mozgáskorlátozottak vagy korlátozott kézügyességgel rendelkezők, akik nem tudnak megbízhatóan többujjas vagy összetett gesztusokat végrehajtani. Ide tartoznak a remegéssel, bénulással élők vagy stylus-t és egykapcsolós beviteli eszközt használók.

Másodlagos előnyök: A gesztusok egyszerűsítése segít a korlátozott beviteli képességekkel rendelkező eszközöket használóknak és olyan helyzetekben, ahol a többujjas gesztusok kényelmetlenek (pl. egykezes eszközhasználat).

Tesztelés

  1. Manuális gesztus tesztelés: Azonosítsd az összes többujjas vagy útvonal-alapú gesztust igénylő UI elemet. Próbáld meg ugyanazt a funkciót egypontos bevitellel (kattintás, érintés vagy billentyűzet) végrehajtani
  2. Segítő technológia tesztelés: Használj segítő eszközöket (kapcsoló eszközök, stylus, billentyűzetes navigáció) a gesztus-alapú vezérlők működtetéséhez
  3. Felhasználó szimuláció: Használd a böngésző fejlesztői eszközeit vagy eszköz szimulátorokat egypontos bevitel szimulálására és a UI funkcionalitás tesztelésére
  4. Kód áttekintés: Vizsgáld meg a JavaScript eseménykezelőket vagy érintési esemény figyelőket, hogy léteznek-e alternatívák az összetett gesztusokhoz
  5. Felhasználói visszajelzés: Gyűjts visszajelzéseket mozgáskorlátozottaktól, hogy megerősítsd a könnyű használhatóságot összetett gesztusok nélkül

Jó megoldások

1. Húzás törléshez alternatív gombbal

<!-- Jó: Látható törlés gomb a húzás gesztus mellett -->
<div class="lista-container">
  <div class="lista-elem" tabindex="0" data-id="1">
    <span class="elem-tartalom">1. lista elem</span>
    <button class="torles-gomb" 
            aria-label="1. lista elem törlése"
            onclick="elemTorles(1)">
      🗑️ Törlés
    </button>
  </div>
  
  <div class="lista-elem" tabindex="0" data-id="2">
    <span class="elem-tartalom">2. lista elem</span>
    <button class="torles-gomb" 
            aria-label="2. lista elem törlése"
            onclick="elemTorles(2)">
      🗑️ Törlés
    </button>
  </div>
  
  <div class="lista-elem" tabindex="0" data-id="3">
    <span class="elem-tartalom">3. lista elem</span>
    <button class="torles-gomb" 
            aria-label="3. lista elem törlése"
            onclick="elemTorles(3)">
      🗑️ Törlés
    </button>
  </div>
</div>

<style>
.lista-container {
  max-width: 400px;
  margin: 20px auto;
}

.lista-elem {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px;
  border: 1px solid #ddd;
  border-radius: 4px;
  margin-bottom: 8px;
  background: white;
  /* Támogatja mind a húzást, mind a gomb kattintást */
  touch-action: pan-x pan-y; /* Engedélyezi a húzást */
}

.lista-elem:focus {
  outline: 2px solid #007bff;
  outline-offset: 2px;
}

.elem-tartalom {
  flex: 1;
  padding-right: 10px;
}

.torles-gomb {
  background: #dc3545;
  color: white;
  border: none;
  padding: 6px 12px;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
}

.torles-gomb:hover {
  background: #c82333;
}

.torles-gomb:focus {
  outline: 2px solid #fff;
  outline-offset: 2px;
  box-shadow: 0 0 0 4px #dc3545;
}

/* Húzás animáció opcionális */
.lista-elem.huzva {
  transform: translateX(-100px);
  transition: transform 0.3s ease;
}
</style>

<script>
// Egypontos alternatíva: gomb kattintás
function elemTorles(id) {
  const elem = document.querySelector(`[data-id="${id}"]`);
  if (elem && confirm('Biztosan törlöd ezt az elemet?')) {
    elem.remove();
  }
}

// Opcionális: húzás gesztus támogatása (mint plusz funkció)
document.querySelectorAll('.lista-elem').forEach(elem => {
  let startX = 0;
  let currentX = 0;
  let isSwipeing = false;
  
  // Érintés kezdete
  elem.addEventListener('touchstart', function(e) {
    startX = e.touches[0].clientX;
    isSwipeing = true;
  });
  
  // Érintés mozgatása
  elem.addEventListener('touchmove', function(e) {
    if (!isSwipeing) return;
    currentX = e.touches[0].clientX;
    const diffX = startX - currentX;
    
    if (diffX > 0) {
      this.style.transform = `translateX(-${Math.min(diffX, 100)}px)`;
    }
  });
  
  // Érintés vége
  elem.addEventListener('touchend', function(e) {
    if (!isSwipeing) return;
    isSwipeing = false;
    
    const diffX = startX - currentX;
    if (diffX > 50) {
      // Húzás elegendő volt
      const id = this.getAttribute('data-id');
      elemTorles(id);
    } else {
      // Vissza eredeti pozícióba
      this.style.transform = 'translateX(0)';
    }
  });
});
</script>

A felhasználók, akik nem tudnak húzni, továbbra is törölhetik az elemet a gombra kattintva. A húzás csak kiegészítő funkcióként szolgál.

2. Csípés nagyításhoz és nagyítás vezérlőkkel

<!-- Jó: Nagyítás gombok a csípés gesztus mellett -->
<div class="nagyithato-container">
  <div class="kepgaleria">
    <img id="nagyithato-kep" 
         src="/images/pelda-kep.jpg" 
         alt="Nagyítható példa kép"
         style="width: 100%; max-width: 500px; transform-origin: center;">
  </div>
  
  <div class="nagyitas-vezerlok">
    <button id="kicsinyites" 
            aria-label="Kép kicsinyítése"
            onclick="zoomOut()">
      🔍➖ Kicsinyítés
    </button>
    
    <span id="nagyitas-szint" aria-live="polite">100%</span>
    
    <button id="nagyitas" 
            aria-label="Kép nagyítása"
            onclick="zoomIn()">
      🔍➕ Nagyítás
    </button>
    
    <button id="eredeti-meret" 
            aria-label="Eredeti méret visszaállítása"
            onclick="resetZoom()">
      🔄 Eredeti méret
    </button>
  </div>
  
  <div class="billentyuzet-help">
    <small>
      Billentyűzet: + nagyítás, - kicsinyítés, 0 eredeti méret
    </small>
  </div>
</div>

<style>
.nagyithato-container {
  max-width: 600px;
  margin: 20px auto;
  text-align: center;
}

.kepgaleria {
  border: 2px solid #ddd;
  border-radius: 8px;
  padding: 10px;
  margin-bottom: 15px;
  overflow: auto;
  /* Támogatja mind a csípést, mind a gombokat */
  touch-action: manipulation; /* Engedélyezi a csípés gesztust */
}

.nagyitas-vezerlok {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 10px;
  margin-bottom: 10px;
}

.nagyitas-vezerlok button {
  background: #007bff;
  color: white;
  border: none;
  padding: 8px 16px;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
}

.nagyitas-vezerlok button:hover {
  background: #0056b3;
}

.nagyitas-vezerlok button:focus {
  outline: 2px solid #ffc107;
  outline-offset: 2px;
}

.nagyitas-vezerlok button:disabled {
  background: #6c757d;
  cursor: not-allowed;
}

#nagyitas-szint {
  background: #f8f9fa;
  padding: 8px 12px;
  border-radius: 4px;
  border: 1px solid #dee2e6;
  font-weight: bold;
  min-width: 60px;
}

.billentyuzet-help {
  color: #6c757d;
  margin-top: 10px;
}
</style>

<script>
let aktualisNagyitas = 1; // 100%
const minNagyitas = 0.5; // 50%
const maxNagyitas = 3; // 300%
const nagyitasLepes = 0.25; // 25% lépések

// Egypontos alternatívák: gombok
function zoomIn() {
  if (aktualisNagyitas < maxNagyitas) {
    aktualisNagyitas += nagyitasLepes;
    updateZoom();
  }
}

function zoomOut() {
  if (aktualisNagyitas > minNagyitas) {
    aktualisNagyitas -= nagyitasLepes;
    updateZoom();
  }
}

function resetZoom() {
  aktualisNagyitas = 1;
  updateZoom();
}

function updateZoom() {
  const kep = document.getElementById('nagyithato-kep');
  const szintElem = document.getElementById('nagyitas-szint');
  
  kep.style.transform = `scale(${aktualisNagyitas})`;
  szintElem.textContent = `${Math.round(aktualisNagyitas * 100)}%`;
  
  // Gombok állapotának frissítése
  document.getElementById('kicsinyites').disabled = aktualisNagyitas <= minNagyitas;
  document.getElementById('nagyitas').disabled = aktualisNagyitas >= maxNagyitas;
}

// Billentyűzet támogatás
document.addEventListener('keydown', function(e) {
  if (e.target.closest('.nagyithato-container')) {
    switch(e.key) {
      case '+':
      case '=':
        e.preventDefault();
        zoomIn();
        break;
      case '-':
        e.preventDefault();
        zoomOut();
        break;
      case '0':
        e.preventDefault();
        resetZoom();
        break;
    }
  }
});

// Opcionális: csípés gesztus támogatása (plusz funkció)
const kep = document.getElementById('nagyithato-kep');
let kezdetiTavolsag = 0;
let kezdetiNagyitas = 1;

kep.addEventListener('touchstart', function(e) {
  if (e.touches.length === 2) {
    const touch1 = e.touches[0];
    const touch2 = e.touches[1];
    kezdetiTavolsag = Math.hypot(
      touch2.clientX - touch1.clientX,
      touch2.clientY - touch1.clientY
    );
    kezdetiNagyitas = aktualisNagyitas;
  }
});

kep.addEventListener('touchmove', function(e) {
  if (e.touches.length === 2) {
    e.preventDefault();
    
    const touch1 = e.touches[0];
    const touch2 = e.touches[1];
    const jelenlegi_tavolsag = Math.hypot(
      touch2.clientX - touch1.clientX,
      touch2.clientY - touch1.clientY
    );
    
    const arany = jelenlegi_tavolsag / kezdetiTavolsag;
    const ujNagyitas = kezdetiNagyitas * arany;
    
    aktualisNagyitas = Math.min(Math.max(ujNagyitas, minNagyitas), maxNagyitas);
    updateZoom();
  }
});

// Kezdeti állapot beállítása
updateZoom();
</script>

A felhasználók gombokkal, vagy billentyűzettel is nagyíthatnak a csípés gesztus helyett. A csípés csak kiegészítő lehetőségként szolgál.

3. Forgatás gesztus és forgatás gombokkal

<!-- Jó: Forgatás gombok a forgatás gesztus mellett -->
<div class="forgathato-container">
  <div class="kep-kontener">
    <img id="forgathato-kep" 
         src="/images/forgathato-kep.jpg" 
         alt="Forgatható kép"
         tabindex="0"
         aria-describedby="forgatas-info"
         style="transition: transform 0.3s ease;">
  </div>
  
  <div id="forgatas-info" class="sr-only">
    Forgatható kép. Használd a forgatás gombokat vagy a nyíl billentyűket.
  </div>
  
  <div class="forgatas-vezerlok">
    <button id="balra-forgatas" 
            aria-label="Kép forgatása balra 90 fokkal"
            onclick="rotateLeft()">
      ↺ Balra forgatás
    </button>
    
    <span id="forgatas-szog" aria-live="polite">0°</span>
    
    <button id="jobbra-forgatas" 
            aria-label="Kép forgatása jobbra 90 fokkal"
            onclick="rotateRight()">
      ↻ Jobbra forgatás
    </button>
    
    <button id="forgatas-reset" 
            aria-label="Forgatás visszaállítása eredeti pozícióba"
            onclick="resetRotation()">
      🔄 Eredeti pozíció
    </button>
  </div>
  
  <div class="billentyuzet-help">
    <small>
      Billentyűzet: ← balra, → jobbra, ↑ eredeti pozíció
    </small>
  </div>
</div>

<!-- Jó: Térkép forgatás navigációs alkalmazásban -->
<div class="terkep-container">
  <div class="terkep-wrapper">
    <div id="terkep" 
         class="terkep"
         tabindex="0"
         aria-label="Interaktív térkép"
         aria-describedby="terkep-info">
      <!-- Térkép tartalom -->
      <div class="terkep-tartalom">
        📍 Budapest<br>
        🏢 Irodák<br>
        🚗 Parkolók
      </div>
    </div>
    
    <div id="terkep-info" class="sr-only">
      Térkép forgatható az iránytű gombokkal vagy a Shift + nyíl billentyűkkel.
    </div>
  </div>
  
  <div class="terkep-vezerlok">
    <div class="iranytu">
      <button class="iranytu-gomb iranytu-eszak" 
              aria-label="Térkép forgatása északra"
              onclick="mapRotateToNorth()">
        ⬆️ É
      </button>
      <button class="iranytu-gomb iranytu-kelet" 
              aria-label="Térkép forgatása keletre"
              onclick="mapRotateToEast()">
        ➡️ K
      </button>
      <button class="iranytu-gomb iranytu-del" 
              aria-label="Térkép forgatása délre"
              onclick="mapRotateToSouth()">
        ⬇️ D
      </button>
      <button class="iranytu-gomb iranytu-nyugat" 
              aria-label="Térkép forgatása nyugatra"
              onclick="mapRotateToWest()">
        ⬅️ Ny
      </button>
    </div>
    
    <button id="auto-irany" 
            aria-label="Automatikus irány követés"
            onclick="autoOrientation()">
      🧭 Auto irány
    </button>
  </div>
</div>

<style>
.forgathato-container, .terkep-container {
  max-width: 400px;
  margin: 20px auto;
  text-align: center;
}

.kep-kontener, .terkep-wrapper {
  border: 2px solid #ddd;
  border-radius: 8px;
  padding: 20px;
  margin-bottom: 15px;
  background: #f8f9fa;
  /* Támogatja mind a forgatást, mind a gombokat */
}

#forgathato-kep {
  max-width: 100%;
  height: auto;
  border-radius: 4px;
  /* Forgatás gesztus támogatása opcionális */
  touch-action: manipulation;
}

#forgathato-kep:focus {
  outline: 2px solid #007bff;
  outline-offset: 4px;
}

.forgatas-vezerlok, .terkep-vezerlok {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 10px;
  margin-bottom: 10px;
  flex-wrap: wrap;
}

.forgatas-vezerlok button, .terkep-vezerlok button {
  background: #28a745;
  color: white;
  border: none;
  padding: 8px 16px;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
}

.forgatas-vezerlok button:hover, .terkep-vezerlok button:hover {
  background: #218838;
}

.forgatas-vezerlok button:focus, .terkep-vezerlok button:focus {
  outline: 2px solid #ffc107;
  outline-offset: 2px;
}

#forgatas-szog {
  background: #e9ecef;
  padding: 8px 12px;
  border-radius: 4px;
  border: 1px solid #ced4da;
  font-weight: bold;
  min-width: 50px;
}

/* Térkép specifikus stílusok */
.terkep {
  width: 200px;
  height: 200px;
  background: #e3f2fd;
  border: 2px solid #1976d2;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto;
  transition: transform 0.5s ease;
  touch-action: manipulation; /* Forgatás gesztus támogatása */
}

.terkep:focus {
  outline: 2px solid #ff9800;
  outline-offset: 4px;
}

.terkep-tartalom {
  text-align: center;
  font-size: 14px;
  line-height: 1.5;
}

.iranytu {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-template-rows: 1fr 1fr 1fr;
  gap: 2px;
  margin: 0 20px;
}

.iranytu-gomb {
  width: 40px;
  height: 40px;
  font-size: 12px;
  padding: 2px;
}

.iranytu-eszak { grid-column: 2; grid-row: 1; }
.iranytu-kelet { grid-column: 3; grid-row: 2; }
.iranytu-del { grid-column: 2; grid-row: 3; }
.iranytu-nyugat { grid-column: 1; grid-row: 2; }

.billentyuzet-help {
  color: #6c757d;
  margin-top: 10px;
}

.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;
}
</style>

<script>
// Kép forgatás változók
let aktualisSzog = 0; // fokokban

// Egypontos alternatívák: gombok
function rotateLeft() {
  aktualisSzog -= 90;
  if (aktualisSzog < 0) aktualisSzog += 360;
  updateRotation();
}

function rotateRight() {
  aktualisSzog += 90;
  if (aktualisSzog >= 360) aktualisSzog -= 360;
  updateRotation();
}

function resetRotation() {
  aktualisSzog = 0;
  updateRotation();
}

function updateRotation() {
  const kep = document.getElementById('forgathato-kep');
  const szogElem = document.getElementById('forgatas-szog');
  
  kep.style.transform = `rotate(${aktualisSzog}deg)`;
  szogElem.textContent = `${aktualisSzog}°`;
}

// Térkép forgatás változók
let terkepSzog = 0; // 0=észak, 90=kelet, 180=dél, 270=nyugat

function mapRotateToNorth() { terkepSzog = 0; updateMapRotation(); }
function mapRotateToEast() { terkepSzog = 90; updateMapRotation(); }
function mapRotateToSouth() { terkepSzog = 180; updateMapRotation(); }
function mapRotateToWest() { terkepSzog = 270; updateMapRotation(); }

function autoOrientation() {
  // Szimulálja az automatikus irány beállítást
  terkepSzog = 0;
  updateMapRotation();
  alert('Automatikus irány bekapcsolva - térkép mindig észak felé mutat');
}

function updateMapRotation() {
  const terkep = document.getElementById('terkep');
  terkep.style.transform = `rotate(${terkepSzog}deg)`;
}

// Billentyűzet támogatás
document.addEventListener('keydown', function(e) {
  // Kép forgatás billentyűzet támogatás
  if (e.target.id === 'forgathato-kep') {
    switch(e.key) {
      case 'ArrowLeft':
        e.preventDefault();
        rotateLeft();
        break;
      case 'ArrowRight':
        e.preventDefault();
        rotateRight();
        break;
      case 'ArrowUp':
        e.preventDefault();
        resetRotation();
        break;
    }
  }
  
  // Térkép forgatás billentyűzet támogatás (Shift + nyíl)
  if (e.target.id === 'terkep' && e.shiftKey) {
    switch(e.key) {
      case 'ArrowUp':
        e.preventDefault();
        mapRotateToNorth();
        break;
      case 'ArrowRight':
        e.preventDefault();
        mapRotateToEast();
        break;
      case 'ArrowDown':
        e.preventDefault();
        mapRotateToSouth();
        break;
      case 'ArrowLeft':
        e.preventDefault();
        mapRotateToWest();
        break;
    }
  }
});

// Kezdeti állapot
updateRotation();
updateMapRotation();
</script>

A forgatás elérhető gombokkal és billentyűzettel, így a forgatás gesztust nem alakalmazó felhasználók is használhatják. A gesztus támogatás csak kiegészítő funkció.

4. Karussel/slider navigáció többféle módon

<!-- Jó: Karussel több navigációs móddal -->
<div class="karussel-container">
  <div class="karussel" 
       id="galeria-karussel"
       role="region" 
       aria-label="Képgaléria"
       aria-describedby="karussel-info">
    
    <div id="karussel-info" class="sr-only">
      Képgaléria navigáció: nyíl gombokkal, pontokkal, billentyűzettel vagy húzással.
    </div>
    
    <div class="karussel-wrapper">
      <div class="karussel-slide aktiv" data-slide="0">
        <img src="/images/slide1.jpg" alt="1. dia: Természet">
        <h3>Természet</h3>
      </div>
      <div class="karussel-slide" data-slide="1">
        <img src="/images/slide2.jpg" alt="2. dia: Építészet">
        <h3>Építészet</h3>
      </div>
      <div class="karussel-slide" data-slide="2">
        <img src="/images/slide3.jpg" alt="3. dia: Emberek">
        <h3>Emberek</h3>
      </div>
    </div>
    
    <!-- Nyíl navigáció (egypontos alternatíva) -->
    <button class="karussel-nav karussel-prev" 
            aria-label="Előző dia"
            onclick="prevSlide()">
      ‹
    </button>
    <button class="karussel-nav karussel-next" 
            aria-label="Következő dia"
            onclick="nextSlide()">
      ›
    </button>
  </div>
  
  <!-- Pont navigáció (egypontos alternatíva) -->
  <div class="karussel-dots" role="tablist" aria-label="Dia választás">
    <button class="dot aktiv" 
            role="tab"
            aria-selected="true"
            aria-controls="slide-0"
            aria-label="1. dia megjelenítése"
            onclick="goToSlide(0)"></button>
    <button class="dot" 
            role="tab"
            aria-selected="false"
            aria-controls="slide-1"
            aria-label="2. dia megjelenítése"
            onclick="goToSlide(1)"></button>
    <button class="dot" 
            role="tab"
            aria-selected="false"
            aria-controls="slide-2"
            aria-label="3. dia megjelenítése"
            onclick="goToSlide(2)"></button>
  </div>
  
  <!-- Lejátszás vezérlők -->
  <div class="karussel-controls">
    <button id="play-pause" 
            aria-label="Automatikus lejátszás indítása"
            onclick="toggleAutoplay()">
      ▶️ Lejátszás
    </button>
    <button aria-label="Karussel visszaállítása első diára"
            onclick="goToSlide(0)">
      ⏮️ Első dia
    </button>
  </div>
  
  <div class="billentyuzet-help">
    <small>
      Billentyűzet: ← előző, → következő, Home első, End utolsó, Space lejátszás
    </small>
  </div>
</div>

<style>
.karussel-container {
  max-width: 600px;
  margin: 20px auto;
}

.karussel {
  position: relative;
  background: #f8f9fa;
  border-radius: 8px;
  overflow: hidden;
  /* Támogatja mind a húzást, mind a gombokat */
  touch-action: pan-y pinch-zoom; /* Engedélyezi vízszintes húzást */
}

.karussel-wrapper {
  display: flex;
  transition: transform 0.3s ease;
  width: 300%; /* 3 dia = 300% */
}

.karussel-slide {
  width: 33.333%; /* 100% / 3 dia */
  text-align: center;
  padding: 20px;
}

.karussel-slide.aktiv {
  /* Aktív dia stílusok */
}

.karussel-slide img {
  width: 100%;
  max-width: 300px;
  height: 200px;
  object-fit: cover;
  border-radius: 4px;
}

.karussel-slide h3 {
  margin: 10px 0 0 0;
  color: #333;
}

/* Nyíl navigáció */
.karussel-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  background: rgba(0, 0, 0, 0.7);
  color: white;
  border: none;
  width: 40px;
  height: 40px;
  font-size: 24px;
  cursor: pointer;
  border-radius: 50%;
  transition: background 0.3s ease;
}

.karussel-nav:hover {
  background: rgba(0, 0, 0, 0.9);
}

.karussel-nav:focus {
  outline: 2px solid #ffc107;
  outline-offset: 2px;
}

.karussel-prev {
  left: 10px;
}

.karussel-next {
  right: 10px;
}

.karussel-nav:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Pont navigáció */
.karussel-dots {
  text-align: center;
  margin: 15px 0;
}

.dot {
  background: #ccc;
  border: none;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  margin: 0 5px;
  cursor: pointer;
  transition: background 0.3s ease;
}

.dot:hover {
  background: #999;
}

.dot:focus {
  outline: 2px solid #007bff;
  outline-offset: 2px;
}

.dot.aktiv {
  background: #007bff;
}

/* Vezérlők */
.karussel-controls {
  text-align: center;
  margin-bottom: 10px;
}

.karussel-controls button {
  background: #6c757d;
  color: white;
  border: none;
  padding: 8px 16px;
  border-radius: 4px;
  margin: 0 5px;
  cursor: pointer;
}

.karussel-controls button:hover {
  background: #5a6268;
}

.karussel-controls button:focus {
  outline: 2px solid #ffc107;
  outline-offset: 2px;
}

.billentyuzet-help {
  text-align: center;
  color: #6c757d;
  margin-top: 10px;
}
</style>

<script>
let aktivisDia = 0;
const osszDia = 3;
let autoplayInterval = null;
let isAutoplayActive = false;

// Egypontos alternatívák: gombok és billentyűzet
function nextSlide() {
  aktivisDia = (aktivisDia + 1) % osszDia;
  updateSlide();
}

function prevSlide() {
  aktivisDia = (aktivisDia - 1 + osszDia) % osszDia;
  updateSlide();
}

function goToSlide(slideIndex) {
  aktivisDia = slideIndex;
  updateSlide();
}

function updateSlide() {
  const wrapper = document.querySelector('.karussel-wrapper');
  const dots = document.querySelectorAll('.dot');
  const slides = document.querySelectorAll('.karussel-slide');
  
  // Dia mozgatása
  wrapper.style.transform = `translateX(-${aktivisDia * 33.333}%)`;
  
  // Aktív állapotok frissítése
  dots.forEach((dot, index) => {
    dot.classList.toggle('aktiv', index === aktivisDia);
    dot.setAttribute('aria-selected', index === aktivisDia);
  });
  
  slides.forEach((slide, index) => {
    slide.classList.toggle('aktiv', index === aktivisDia);
  });
  
  // Navigációs gombok állapota
  const prevBtn = document.querySelector('.karussel-prev');
  const nextBtn = document.querySelector('.karussel-next');
  
  // Opcionális: első/utolsó dia esetén letiltás
  // prevBtn.disabled = aktivisDia === 0;
  // nextBtn.disabled = aktivisDia === osszDia - 1;
}

function toggleAutoplay() {
  const playBtn = document.getElementById('play-pause');
  
  if (isAutoplayActive) {
    clearInterval(autoplayInterval);
    playBtn.textContent = '▶️ Lejátszás';
    playBtn.setAttribute('aria-label', 'Automatikus lejátszás indítása');
    isAutoplayActive = false;
  } else {
    autoplayInterval = setInterval(nextSlide, 3000);
    playBtn.textContent = '⏸️ Szünet';
    playBtn.setAttribute('aria-label', 'Automatikus lejátszás megállítása');
    isAutoplayActive = true;
  }
}

// Billentyűzet támogatás
document.addEventListener('keydown', function(e) {
  if (e.target.closest('.karussel-container')) {
    switch(e.key) {
      case 'ArrowLeft':
        e.preventDefault();
        prevSlide();
        break;
      case 'ArrowRight':
        e.preventDefault();
        nextSlide();
        break;
      case 'Home':
        e.preventDefault();
        goToSlide(0);
        break;
      case 'End':
        e.preventDefault();
        goToSlide(osszDia - 1);
        break;
      case ' ':
        e.preventDefault();
        toggleAutoplay();
        break;
    }
  }
});

// Opcionális: húzás gesztus támogatása (plusz funkció)
let startX = 0;
let startY = 0;
let isSwipeing = false;

const karussel = document.querySelector('.karussel');

karussel.addEventListener('touchstart', function(e) {
  startX = e.touches[0].clientX;
  startY = e.touches[0].clientY;
  isSwipeing = true;
});

karussel.addEventListener('touchmove', function(e) {
  if (!isSwipeing) return;
  
  const currentX = e.touches[0].clientX;
  const currentY = e.touches[0].clientY;
  const diffX = Math.abs(currentX - startX);
  const diffY = Math.abs(currentY - startY);
  
  // Csak vízszintes húzás
  if (diffX > diffY && diffX > 30) {
    e.preventDefault();
  }
});

karussel.addEventListener('touchend', function(e) {
  if (!isSwipeing) return;
  isSwipeing = false;
  
  const endX = e.changedTouches[0].clientX;
  const diffX = startX - endX;
  
  if (Math.abs(diffX) > 50) {
    if (diffX > 0) {
      nextSlide(); // Balra húzás = következő
    } else {
      prevSlide(); // Jobbra húzás = előző
    }
  }
});

// Kezdeti állapot
updateSlide();
</script>

A karussel többféle navigációs módot biztosít: nyíl gombok, pont navigáció, billentyűzet és opcionálisan húzás gesztus. Minden felhasználó megtalálja a maga számára megfelelő módot.

Helytelen megoldások

Csak gesztus-alapú interakciók

<!-- Rossz: Karussel csak húzással működik -->
<div id="rossz-karussel" ontouchstart="handleSwipe(event)">
  <div class="slide">1. dia</div>
  <div class="slide">2. dia</div>
  <div class="slide">3. dia</div>
</div>
<!-- Nincsenek gombok vagy billentyűzet alternatívák -->

<!-- Rossz: Csak csípés nagyítás -->
<img id="rossz-zoom" src="kep.jpg" alt="Kép">
<script>
// Rossz: csak többujjas gesztus támogatás
document.getElementById('rossz-zoom').addEventListener('gesturechange', function(e) {
  this.style.transform = `scale(${e.scale})`;
  // Nincs egypontos alternatíva
});
</script>

<!-- Rossz: Csak forgatás gesztus -->
<div id="rossz-forgatas" style="touch-action: none;">
  Forgatható elem
</div>
<script>
// Rossz: csak összetett gesztus
let startAngle = 0;
document.getElementById('rossz-forgatas').addEventListener('touchstart', function(e) {
  if (e.touches.length === 2) {
    // Csak kétujjas forgatás támogatva
    // Nincs egypontos alternatíva
  }
});
</script>

Probléma: A felhasználók, akik nem tudnak húzni, csípni vagy forgatni, egyáltalán nem tudják használni ezeket a funkciókat.

Összetett gesztus visszaállítás nélkül

<!-- Rossz: Többérintéses gesztus egypontos visszaállítás nélkül -->
<div id="complex-gesture" style="width: 200px; height: 200px; background: #ccc;">
  Összetett gesztus terület
</div>

<script>
// Rossz: csak többérintéses interakció
const elem = document.getElementById('complex-gesture');

elem.addEventListener('gesturechange', function(event) {
  // Csak multi-touch gesztus kezelése
  this.style.transform = `scale(${event.scale}) rotate(${event.rotation}deg)`;
  // Nincs egypontos visszaállítás lehetőség
});

// Rossz: komplex útvonal gesztus
let path = [];
elem.addEventListener('touchmove', function(e) {
  if (e.touches.length === 1) {
    path.push({x: e.touches[0].clientX, y: e.touches[0].clientY});
    
    // Csak összetett útvonal minták elismerése
    if (isComplexPattern(path)) {
      // Működés aktiválása
      // Nincs egyszerű alternatíva
    }
  }
});

function isComplexPattern(path) {
  // Csak összetett minták (pl. "Z" alakú mozgás)
  return path.length > 10; // Túl összetett kritérium
}
</script>

Probléma: A funkcionalitás megtörik azok számára, akiknek nincs többérintéses beviteli lehetőségük.

Nem támogatott a billentyűzet vagy egypontos kezelés

<!-- Rossz: Interaktív elemek csak gesztusokra támaszkodva -->
<div class="gesture-only-interface">
  <div class="swipeable-card" data-action="delete">
    Húzd balra a törléshez
    <!-- Nincs törlés gomb -->
  </div>
  
  <div class="pinchable-content">
    Csípj a nagyításhoz
    <!-- Nincsenek nagyítás gombok -->
  </div>
  
  <div class="rotatable-wheel">
    Forgasd az értékek változtatásához
    <!-- Nincs input mező vagy gombok -->
  </div>
</div>

<!-- Rossz: Menü csak gesztussal nyitható -->
<div class="hidden-menu" 
     ontouchstart="menuOpen(event)"
     style="display: none;">
  <!-- Menü tartalom -->
</div>

<script>
// Rossz: csak gesztus alapú megnyitás
function menuOpen(e) {
  if (e.touches.length === 3) {
    // Csak három ujjas érintés
    document.querySelector('.hidden-menu').style.display = 'block';
    // Nincs gomb vagy billentyűzet alternatíva
  }
}

// Rossz: képernyő szélén gesztusok
document.addEventListener('touchstart', function(e) {
  const touch = e.touches[0];
  
  // Csak szél gesztusok
  if (touch.clientX < 20) {
    // Baloldali szél gesztus
    // Nincs alternatív aktiválási mód
  }
  
  if (touch.clientX > window.innerWidth - 20) {
    // Jobboldali szél gesztus  
    // Szintén nincs alternatíva
  }
});
</script>

Probléma: Az interaktív elemek, amelyek kizárólag többérintéses gesztusokra támaszkodnak, billentyűzet, vagy egér/egyérintéses alternatívák nélkül, kizárnak számos felhasználót.

Nehezen felismerhető vagy dokumentálatlan gesztusok

<!-- Rossz: Rejtett vagy nem intuitív gesztusok -->
<div class="secret-gestures" style="width: 300px; height: 200px; background: #f0f0f0;">
  <p>Rejtett funkciók területe</p>
  <!-- Nincs útmutatás a gesztusokhoz -->
</div>

<script>
// Rossz: összetett, nem intuitív gesztus kombinációk
const area = document.querySelector('.secret-gestures');

area.addEventListener('touchstart', function(e) {
  // Rossz: túl specifikus gesztus követelmények
  if (e.touches.length === 2) {
    const touch1 = e.touches[0];
    const touch2 = e.touches[1];
    
    // Rossz: pontos pozíció követelmény
    if (touch1.clientX < 100 && touch2.clientX > 200) {
      // Csak ha az első érintés bal oldalon, második jobb oldalon
      // Nincs alternatíva vagy útmutatás
      secretFunction();
    }
  }
});

// Rossz: időzített gesztus szekvenciák
let gestureSequence = [];
let lastGestureTime = 0;

area.addEventListener('touchend', function(e) {
  const now = Date.now();
  
  // Rossz: időzített gesztus szekvencia
  if (now - lastGestureTime < 500) {
    gestureSequence.push('tap');
    
    // Rossz: specifikus szekvencia követelmény
    if (gestureSequence.join(',') === 'tap,tap,swipe,pinch') {
      // Csak ha pontos szekvenciát követnek
      // Nincs egyszerű alternatíva
      advancedFunction();
    }
  } else {
    gestureSequence = [];
  }
  
  lastGestureTime = now;
});

function secretFunction() {
  alert('Rejtett funkció aktiválva!');
  // Ezt a funkciót csak gesztussal lehet elérni
}

function advancedFunction() {
  alert('Haladó funkció aktiválva!');
  // Ez is csak összetett gesztus szekvenciával
}
</script>

Probléma: A rejtett, összetett, vagy nem dokumentált gesztusok akadálymentességi és használhatósági problémákat okoznak. Minden funkciónak elérhető alternatívával kell rendelkeznie.

Források

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