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

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