1.4.13 - Tartalom mutató mozgás közben

Röviden a szabványpontról

A WCAG 1.4.13 (Content on Hover or Focus) előírja, hogy minden olyan kiegészítő tartalom, amely megjelenik, amikor a felhasználó egy elem fölé viszi az egeret vagy arra fókuszál (mint tooltip-ek, legördülő menük vagy felugró ablakok), meg kell feleljen bizonyos akadálymentességi követelményeknek. Ez a tartalom legyen:

  • Elutasítható: A felhasználóknak képesnek kell lenniük a tartalom egyszerű elutasítására
  • Lebegtethetőre: A felhasználóknak képesnek kell lenniük a mutatót a tartalomba mozgatni anélkül, hogy az eltűnne
  • Állandó: A tartalomnak láthatónak kell maradnia, amíg el nem utasítják vagy a fókusz el nem mozdul

Cél: Biztosítani, hogy a billentyűzetes navigációra vagy kisegítő technológiákra támaszkodó felhasználók hozzáférjenek és interakcióba lépjenek a hover vagy fókusz által felfedett tartalommal anélkül, hogy az váratlanul eltűnne vagy elérhetetlenné válna.

Kiket érint

Elsődleges felhasználók: Mozgássérült emberek, akik billentyűzettel vagy alternatív beviteli eszközökkel navigálnak, kognitív fogyatékossággal élő felhasználók, akiknek állandó kontextusra van szükségük, és képernyőolvasó felhasználók, akik billentyűzet fókuszra támaszkodnak.

Másodlagos előnyök: Javított használhatóság minden felhasználó számára, beleértve az érintőképernyős eszközöket használókat vagy átmeneti fogyatékossággal élőket, az interaktív tartalom kiszámíthatóbbá és kezelhetőbbé tételével.

Tesztelés

Az 1.4.13 megfelelőségének ellenőrzéséhez próbáld ki a következő tesztelési módszereket:

  1. Csak billentyűzetes navigáció: Használd a billentyűzetet (Tab, Shift+Tab, nyilak) olyan elemekre navigáláshoz, amelyek fókuszra tartalmat fednek fel. Erősítsd meg, hogy a tartalom megjelenik és látható marad, amíg fókuszban van
  2. Mutató hover tesztelés: Vidd az egeret az elem fölé és mozgasd a mutatót a felfedett tartalomba. Ellenőrizd, hogy a tartalom nem tűnik el, amikor a mutatót bele mozgatod
  3. Elutasíthatóság ellenőrzés: Győződj meg róla, hogy van egyértelmű módja a tartalom elutasításának (pl. Escape billentyű nyomása vagy bezárás gomb) és hogy az várt módon eltűnik
  4. Képernyőolvasó teszt: Használj képernyőolvasót az elemre navigáláshoz és erősítsd meg, hogy a kiegészítő tartalom bejelentésre kerül és hozzáférhető
  5. Érintőeszköz teszt: Érintőképernyőn érintsd meg az elemet a tartalom felfedéséhez és ellenőrizd, hogy látható marad és elutasítható

Jó gyakorlatok

1. Tooltip billentyűzet támogatással

<div class="tooltip-container">
  <button aria-describedby="tooltip1" id="info-btn" class="info-button">
    ℹ️ Információ
  </button>
  <div role="tooltip" id="tooltip1" class="tooltip" hidden>
    Ez a tooltip tartalom magyarázza a gomb funkcióját. Látható marad fókusz alatt és hover állapotban.
  </div>
</div>

<style>
.tooltip-container {
  position: relative;
  display: inline-block;
  margin: 20px;
}

.info-button {
  background-color: #007bff;
  color: white;
  border: none;
  padding: 10px 15px;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
}

.info-button:hover, .info-button:focus {
  background-color: #0056b3;
  outline: 2px solid #80bdff;
  outline-offset: 2px;
}

.tooltip {
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  background-color: #333;
  color: white;
  padding: 8px 12px;
  border-radius: 4px;
  font-size: 14px;
  white-space: nowrap;
  z-index: 1000;
  margin-bottom: 5px;
  max-width: 200px;
  white-space: normal;
}

.tooltip::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 50%;
  transform: translateX(-50%);
  border: 5px solid transparent;
  border-top-color: #333;
}

.tooltip:not([hidden]) {
  display: block;
}
</style>

<script>
document.addEventListener('DOMContentLoaded', function() {
  const btn = document.getElementById('info-btn');
  const tooltip = document.getElementById('tooltip1');
  let hideTimeout;

  function showTooltip() {
    clearTimeout(hideTimeout);
    tooltip.hidden = false;
  }

  function hideTooltip() {
    hideTimeout = setTimeout(() => {
      tooltip.hidden = true;
    }, 100);
  }

  // Megjelenítés események
  btn.addEventListener('focus', showTooltip);
  btn.addEventListener('mouseenter', showTooltip);
  tooltip.addEventListener('mouseenter', showTooltip);

  // Elrejtés események
  btn.addEventListener('blur', hideTooltip);
  btn.addEventListener('mouseleave', hideTooltip);
  tooltip.addEventListener('mouseleave', hideTooltip);

  // Escape billentyűvel való elutasítás
  document.addEventListener('keydown', function(e) {
    if (e.key === 'Escape' && !tooltip.hidden) {
      tooltip.hidden = true;
      btn.focus();
    }
  });
});
</script>

Magyarázat: A tooltip megjelenik hover és fókusz esetén egyaránt. Csak akkor tűnik el, amikor a felhasználó elmozgatja a fókuszt vagy az egeret, és az Escape billentyűvel elutasítható.

2. Legördülő menü, amely nyitva marad hover vagy fókusz alatt

<nav class="dropdown-nav">
  <ul class="main-menu">
    <li class="menu-item">
      <button tabindex="0" aria-haspopup="true" aria-expanded="false" id="services-menu" class="menu-button">
        Szolgáltatások ▼
      </button>
      <ul id="services-submenu" class="submenu" hidden>
        <li><a href="/webfejlesztes" class="submenu-link">Webfejlesztés</a></li>
        <li><a href="/seo" class="submenu-link">SEO optimalizálás</a></li>
        <li><a href="/karbantartas" class="submenu-link">Karbantartás</a></li>
      </ul>
    </li>
  </ul>
</nav>

<style>
.dropdown-nav {
  background-color: #f8f9fa;
  padding: 10px;
  margin: 20px 0;
}

.main-menu {
  list-style: none;
  margin: 0;
  padding: 0;
}

.menu-item {
  position: relative;
  display: inline-block;
}

.menu-button {
  background-color: #007bff;
  color: white;
  border: none;
  padding: 10px 15px;
  cursor: pointer;
  border-radius: 4px;
  font-size: 16px;
}

.menu-button:hover, .menu-button:focus {
  background-color: #0056b3;
  outline: 2px solid #80bdff;
  outline-offset: 2px;
}

.submenu {
  position: absolute;
  top: 100%;
  left: 0;
  background-color: white;
  border: 1px solid #dee2e6;
  border-radius: 4px;
  box-shadow: 0 2px 10px rgba(0,0,0,0.1);
  min-width: 200px;
  z-index: 1000;
  list-style: none;
  margin: 0;
  padding: 5px 0;
}

.submenu:not([hidden]) {
  display: block;
}

.submenu-link {
  display: block;
  padding: 10px 15px;
  color: #333;
  text-decoration: none;
  transition: background-color 0.2s;
}

.submenu-link:hover, .submenu-link:focus {
  background-color: #f8f9fa;
  outline: 2px solid #007bff;
  outline-offset: -2px;
}
</style>

<script>
document.addEventListener('DOMContentLoaded', function() {
  const menu = document.getElementById('services-menu');
  const submenu = document.getElementById('services-submenu');
  const menuItem = menu.closest('.menu-item');
  let closeTimeout;

  function openMenu() {
    clearTimeout(closeTimeout);
    submenu.hidden = false;
    menu.setAttribute('aria-expanded', 'true');
  }

  function closeMenu() {
    closeTimeout = setTimeout(() => {
      submenu.hidden = true;
      menu.setAttribute('aria-expanded', 'false');
    }, 100);
  }

  function immediateClose() {
    clearTimeout(closeTimeout);
    submenu.hidden = true;
    menu.setAttribute('aria-expanded', 'false');
  }

  // Megnyitás események
  menu.addEventListener('focus', openMenu);
  menu.addEventListener('mouseenter', openMenu);
  menuItem.addEventListener('mouseenter', openMenu);

  // Bezárás események
  menu.addEventListener('blur', closeMenu);
  menuItem.addEventListener('mouseleave', closeMenu);

  // Escape billentyűvel bezárás
  document.addEventListener('keydown', function(e) {
    if (e.key === 'Escape' && !submenu.hidden) {
      immediateClose();
      menu.focus();
    }
  });

  // Submenu linkek kezelése
  const submenuLinks = submenu.querySelectorAll('.submenu-link');
  submenuLinks.forEach(link => {
    link.addEventListener('blur', function(e) {
      // Ha az utolsó elemről lépünk ki és nem maradunk az almenün belül
      if (!submenu.contains(e.relatedTarget)) {
        closeMenu();
      }
    });
  });
});
</script>

Magyarázat: Az almenü nyitva marad, amíg hover vagy fókusz állapotban van. Az ARIA attribútumok kommunikálják a menü állapotát, és a billentyűzet felhasználók navigálhatnak az almenü elemei között.

3. Elutasítható felugró ablak

<div class="help-section">
  <button id="help-trigger" aria-haspopup="dialog" aria-controls="help-dialog" class="help-button">
    ❓ Súgó
  </button>
  
  <div id="help-dialog" role="dialog" aria-labelledby="help-title" class="help-dialog" hidden>
    <div class="dialog-content">
      <h3 id="help-title">Súgó információ</h3>
      <p>Ez hasznos információ a funkció használatáról. A dialógus nyitva marad, amíg be nem zárod vagy el nem navigálsz róla.</p>
      <button id="close-help" class="close-button">✕ Bezárás</button>
    </div>
  </div>
</div>

<style>
.help-section {
  position: relative;
  margin: 20px;
}

.help-button {
  background-color: #28a745;
  color: white;
  border: none;
  padding: 10px 15px;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
}

.help-button:hover, .help-button:focus {
  background-color: #218838;
  outline: 2px solid #80e5a3;
  outline-offset: 2px;
}

.help-dialog {
  position: absolute;
  top: 100%;
  left: 0;
  background-color: white;
  border: 2px solid #007bff;
  border-radius: 8px;
  box-shadow: 0 4px 20px rgba(0,0,0,0.15);
  padding: 0;
  z-index: 1001;
  min-width: 300px;
  max-width: 400px;
}

.help-dialog:not([hidden]) {
  display: block;
}

.dialog-content {
  padding: 20px;
}

.dialog-content h3 {
  margin: 0 0 10px 0;
  color: #333;
  font-size: 16px;
}

.dialog-content p {
  margin: 0 0 15px 0;
  color: #666;
  line-height: 1.5;
}

.close-button {
  background-color: #dc3545;
  color: white;
  border: none;
  padding: 5px 10px;
  border-radius: 4px;
  cursor: pointer;
  font-size: 12px;
  float: right;
}

.close-button:hover, .close-button:focus {
  background-color: #c82333;
  outline: 2px solid #f5c6cb;
  outline-offset: 2px;
}
</style>

<script>
document.addEventListener('DOMContentLoaded', function() {
  const helpBtn = document.getElementById('help-trigger');
  const helpDialog = document.getElementById('help-dialog');
  const closeBtn = document.getElementById('close-help');
  let isDialogOpen = false;

  function openDialog() {
    helpDialog.hidden = false;
    isDialogOpen = true;
    // Fókusz a dialógusba (első fókuszálható elemre)
    closeBtn.focus();
  }

  function closeDialog() {
    helpDialog.hidden = true;
    isDialogOpen = false;
    // Fókusz visszaadása a kiváltó gombnak
    helpBtn.focus();
  }

  // Dialógus megnyitása
  helpBtn.addEventListener('click', function(e) {
    e.preventDefault();
    openDialog();
  });

  helpBtn.addEventListener('focus', function() {
    setTimeout(openDialog, 500); // Kis késleltetés a véletlenszerű megnyitás elkerülésére
  });

  // Dialógus bezárása
  closeBtn.addEventListener('click', closeDialog);

  // Escape billentyűvel bezárás
  document.addEventListener('keydown', function(e) {
    if (e.key === 'Escape' && isDialogOpen) {
      closeDialog();
    }
  });

  // Kattintás a dialóguson kívül
  document.addEventListener('click', function(e) {
    if (isDialogOpen && !helpDialog.contains(e.target) && e.target !== helpBtn) {
      closeDialog();
    }
  });

  // Fókusz kezelés - bezárás, ha a fókusz elhagyja a dialógust
  helpDialog.addEventListener('focusout', function(e) {
    setTimeout(() => {
      if (isDialogOpen && !helpDialog.contains(document.activeElement)) {
        closeDialog();
      }
    }, 10);
  });
});
</script>

Magyarázat: A dialógus megnyílik fókuszra és bezárás gombbal, Escape billentyűvel vagy fókusz elhagyásával elutasítható. A fókusz kezelés visszatér a kiváltó gombhoz bezárás után.

4. Accordion panel hover és fókusz támogatással

<div class="accordion-container">
  <div class="accordion-item">
    <button id="accordion-btn-1" aria-expanded="false" aria-controls="accordion-panel-1" class="accordion-header">
      Gyakori kérdések ▼
    </button>
    <div id="accordion-panel-1" class="accordion-panel" hidden>
      <p>Itt találod a leggyakrabban feltett kérdéseket és válaszokat. Ez a panel nyitva marad, amíg aktív fókuszban vagy hover állapotban van.</p>
      <ul>
        <li><a href="#faq1">Hogyan regisztrálhatok?</a></li>
        <li><a href="#faq2">Milyen fizetési módokat fogadnak el?</a></li>
        <li><a href="#faq3">Mennyi a szállítási idő?</a></li>
      </ul>
    </div>
  </div>
</div>

<style>
.accordion-container {
  max-width: 500px;
  margin: 20px auto;
  border: 1px solid #dee2e6;
  border-radius: 8px;
  overflow: hidden;
}

.accordion-item {
  position: relative;
}

.accordion-header {
  width: 100%;
  background-color: #f8f9fa;
  border: none;
  padding: 15px 20px;
  text-align: left;
  cursor: pointer;
  font-size: 16px;
  font-weight: 600;
  color: #333;
  transition: background-color 0.2s;
}

.accordion-header:hover, .accordion-header:focus {
  background-color: #e9ecef;
  outline: 2px solid #007bff;
  outline-offset: -2px;
}

.accordion-header[aria-expanded="true"] {
  background-color: #007bff;
  color: white;
}

.accordion-panel {
  background-color: white;
  padding: 20px;
  border-top: 1px solid #dee2e6;
}

.accordion-panel:not([hidden]) {
  display: block;
}

.accordion-panel ul {
  list-style-type: disc;
  padding-left: 20px;
}

.accordion-panel li {
  margin-bottom: 8px;
}

.accordion-panel a {
  color: #007bff;
  text-decoration: none;
}

.accordion-panel a:hover, .accordion-panel a:focus {
  text-decoration: underline;
  outline: 2px solid #80bdff;
  outline-offset: 2px;
}
</style>

<script>
document.addEventListener('DOMContentLoaded', function() {
  const accordionBtn = document.getElementById('accordion-btn-1');
  const accordionPanel = document.getElementById('accordion-panel-1');
  const accordionItem = accordionBtn.closest('.accordion-item');
  let closeTimeout;
  let isExpanded = false;

  function expandPanel() {
    clearTimeout(closeTimeout);
    accordionPanel.hidden = false;
    accordionBtn.setAttribute('aria-expanded', 'true');
    accordionBtn.innerHTML = 'Gyakori kérdések ▲';
    isExpanded = true;
  }

  function collapsePanel() {
    closeTimeout = setTimeout(() => {
      accordionPanel.hidden = true;
      accordionBtn.setAttribute('aria-expanded', 'false');
      accordionBtn.innerHTML = 'Gyakori kérdések ▼';
      isExpanded = false;
    }, 200);
  }

  function immediateCollapse() {
    clearTimeout(closeTimeout);
    accordionPanel.hidden = true;
    accordionBtn.setAttribute('aria-expanded', 'false');
    accordionBtn.innerHTML = 'Gyakori kérdések ▼';
    isExpanded = false;
  }

  // Kattintás kezelés
  accordionBtn.addEventListener('click', function() {
    if (isExpanded) {
      immediateCollapse();
    } else {
      expandPanel();
    }
  });

  // Hover események
  accordionItem.addEventListener('mouseenter', expandPanel);
  accordionItem.addEventListener('mouseleave', collapsePanel);

  // Fókusz események
  accordionBtn.addEventListener('focus', expandPanel);
  
  // Panel fókusz kezelés
  accordionPanel.addEventListener('focusout', function(e) {
    if (!accordionItem.contains(e.relatedTarget)) {
      collapsePanel();
    }
  });

  // Escape billentyű
  document.addEventListener('keydown', function(e) {
    if (e.key === 'Escape' && isExpanded) {
      immediateCollapse();
      accordionBtn.focus();
    }
  });
});
</script>

Magyarázat: Az accordion panel automatikusan kinyílik hover és fókusz esetén, és nyitva marad, amíg a felhasználó aktívan interakcióban van vele.

Rossz gyakorlatok

Tartalom azonnal eltűnik, amikor a mutató elhagyja a kiváltó elemet

<button id="bad-tooltip-trigger">Rossz tooltip</button>
<div id="bad-tooltip" class="bad-tooltip" style="display: none;">
  Ez a tooltip eltűnik, mielőtt elérnéd
</div>

<script>
// ROSSZ MEGVALÓSÍTÁS
document.getElementById('bad-tooltip-trigger').addEventListener('mouseenter', function() {
  document.getElementById('bad-tooltip').style.display = 'block';
});

document.getElementById('bad-tooltip-trigger').addEventListener('mouseleave', function() {
  document.getElementById('bad-tooltip').style.display = 'none'; // Azonnal eltűnik
});
</script>

Probléma: A tartalom azonnal eltűnik, amikor a mutató elhagyja a kiváltó elemet, megakadályozva a felhasználókat abban, hogy interakcióba lépjenek vele.

Nincs billentyűzet támogatás a tartalom felfedéséhez vagy elutasításához

<div class="hover-only-content">
  <span>Csak hover-re reagál</span>
  <div class="hover-tooltip">Csak egérrel elérhető tooltip</div>
</div>

<style>
.hover-tooltip {
  display: none;
  position: absolute;
  background: #333;
  color: white;
  padding: 5px;
}

.hover-only-content:hover .hover-tooltip {
  display: block; /* Csak hover-re jelenik meg, fókuszra nem */
}
</style>

Probléma: A csak hover-re reagáló tartalom kizárja a billentyűzet felhasználókat, akik nem tudják elérni a tartalmat.

Tartalom megjelenik, de nem utasítható el vagy fókusz csapdát okoz

<button id="problematic-trigger">Problémás trigger</button>
<div id="problematic-popup" class="problematic-popup" style="display: none;">
  <p>Ez a popup nem utasítható el megfelelően</p>
  <!-- Nincs bezárás gomb és nem reagál Escape-re -->
</div>

<script>
document.getElementById('problematic-trigger').addEventListener('click', function() {
  document.getElementById('problematic-popup').style.display = 'block';
  // Nincs elutasítási mechanizmus
});
</script>

Probléma: A popup megjelenik, de nincs egyértelmű módja az elutasításnak, ami akadálymentességi problémát és frusztrációt okoz.

Natív title attribútum használata tooltip-ként

<button id="native-title-btn" title="Ez a natív tooltip szöveg">
  Hover me (rossz példa)
</button>

Probléma: A natív title attribútum tooltip nem elérhető billentyűzettel és eltűnik egér elhagyáskor, nem felel meg a kritériumnak.

Rövid időzítés vagy automatikus elrejtés

<button id="auto-hide-trigger">Automatikusan elrejtő tartalom</button>
<div id="auto-hide-content" style="display: none;">
  Ez a tartalom automatikusan eltűnik
</div>

<script>
document.getElementById('auto-hide-trigger').addEventListener('focus', function() {
  const content = document.getElementById('auto-hide-content');
  content.style.display = 'block';
  
  // ROSSZ: Automatikus elrejtés rövid idő után
  setTimeout(() => {
    content.style.display = 'none';
  }, 2000); // 2 másodperc után eltűnik, függetlenül a felhasználó interakciójától
});
</script>

Probléma: Az automatikus elrejtés nem ad elegendő időt a felhasználóknak a tartalom elolvasására vagy interakcióra, különösen a kisegítő technológiát használó felhasználóknak.

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