3.3.1 - Hibajelzés

Röviden a szabványpontról

A WCAG 2.2 Success Criterion 3.3.1 (Error Identification) megköveteli, hogy amikor beviteli hibát észlelünk, a hibát azonosítsuk és szöveges formában ismertessük a felhasználóval. Ez vonatkozik minden felhasználói beviteli űrlapra, interaktív vezérlőre és adatbeviteli pontra a webes tartalomban. A cél, annak biztosítása, hogy azok a felhasználók, akik hibákat ejtenek az űrlapok kitöltése, vagy a bevitelek használata során, világosan tájékoztatva legyenek arról, hogy mit rontottak el, és így a felmerülő hibákat ki is tudják javítani.

Cél: Segíteni a felhasználókat abban, hogy elkerüljék a megtévesztést és frusztrációt azáltal, hogy világos és érthető hibaüzeneteket kapnak, amelyek pontosan megmondják, mit kell javítaniuk. Ez különösen fontos a kisegítő technológiákat használó felhasználók számára.

Mire vonatkozik: Minden olyan webes tartalomra, amely felhasználói adatbevitelt fogad, beleértve az űrlapokat, interaktív vezérlőket, keresőmezőket és bármilyen adatvalidációt igénylő elemet.

Kiket érint

Elsődleges felhasználók: Kognitív fogyatékossággal, tanulási nehézségekkel, gyengénlátással élő emberek vagy azok, akik képernyőolvasóra támaszkodnak, nagy hasznot húznak a világos hibaazonosításból. Segít nekik megérteni, hogy melyik bevitelt kell javítaniuk.

Másodlagos előnyök: Minden felhasználó számára előnyösek a világos hibaüzenetek, melyek javítják az általános használhatóságot és csökkentik a hibákat az űrlap beküldése vagy egyéb interakció során.

Tesztelés

  1. Manuális űrlap tesztelés: Szándékosan érvénytelen adatokat írj be és/vagy hagyd ki a kötelező mezőket és ellenőrizd, hogy megjelennek-e a hibaüzenetek és világosan leírják-e a problémát
  2. Képernyőolvasó tesztelés: Használj képernyőolvasót (pl. NVDA, JAWS, VoiceOver) annak ellenőrzésére, hogy a hibaüzenetek programozottan kapcsolódnak a megfelelő beviteli mezőkhöz és felolvasásra kerülnek-e
  3. Kizárólag billentyűzetes navigáció: Küldj el hibás űrlapokat csak billentyűzet használatával és győződj meg róla, hogy a fókusz a hibaüzenetre vagy annak közelébe kerül
  4. Automatizált eszközök: Használj akadálymentességi tesztelő eszközöket, mint az axe DevTools a hiányzó vagy helytelenül kapcsolt hibaüzenetek észlelésére
  5. Kód vizsgálat: Ellenőrizd, hogy a hibaüzenetek kapcsolódnak a bevitelekhez ARIA attribútumok használatával, mint az aria-describedby vagy a hibaüzenet elhelyezésével a címkékben vagy bevitelek közelében

Jó megoldások

1. Explicit hibaüzenetek beviteli mezőkhöz kapcsolva

<!-- REGISZTRÁCIÓS ŰRLAP - Teljes hibakezeléssel -->
<form class="registration-form" novalidate>
  <div class="form-section">
    <h2>Új fiók regisztrációja</h2>
    
    <!-- Általános hibák megjelenítése -->
    <div id="form-errors" class="form-errors" role="alert" aria-live="polite" style="display: none;">
      <h3>Hibák történtek az űrlap kitöltése során:</h3>
      <ul id="error-list"></ul>
    </div>
  </div>
  
  <div class="form-group">
    <label for="username">Felhasználónév *</label>
    <input 
      type="text" 
      id="username" 
      name="username" 
      required 
      minlength="3"
      maxlength="20"
      pattern="[a-zA-Z0-9]+"
      aria-describedby="username-help username-error"
      autocomplete="username"
    >
    <div id="username-help" class="field-help">
      3-20 karakter, csak betűk és számok használhatók
    </div>
    <div id="username-error" class="error-message" role="alert" style="display: none;"></div>
  </div>
  
  <div class="form-group">
    <label for="email">E-mail cím *</label>
    <input 
      type="email" 
      id="email" 
      name="email" 
      required
      aria-describedby="email-help email-error"
      autocomplete="email"
    >
    <div id="email-help" class="field-help">
      Valós e-mail címet adjon meg (pl. nev@domain.hu)
    </div>
    <div id="email-error" class="error-message" role="alert" style="display: none;"></div>
  </div>
  
  <div class="form-group">
    <label for="password">Jelszó *</label>
    <input 
      type="password" 
      id="password" 
      name="password" 
      required
      minlength="8"
      aria-describedby="password-help password-error"
      autocomplete="new-password"
    >
    <div id="password-help" class="field-help">
      Minimum 8 karakter, tartalmazzon legalább egy számot és egy nagybetűt
    </div>
    <div id="password-error" class="error-message" role="alert" style="display: none;"></div>
  </div>
  
  <div class="form-group">
    <label for="password-confirm">Jelszó megerősítése *</label>
    <input 
      type="password" 
      id="password-confirm" 
      name="password-confirm" 
      required
      aria-describedby="password-confirm-help password-confirm-error"
      autocomplete="new-password"
    >
    <div id="password-confirm-help" class="field-help">
      Írja be újra a jelszót a megerősítéshez
    </div>
    <div id="password-confirm-error" class="error-message" role="alert" style="display: none;"></div>
  </div>
  
  <div class="form-group">
    <label for="birth-date">Születési dátum *</label>
    <input 
      type="date" 
      id="birth-date" 
      name="birth-date" 
      required
      max="2006-01-01"
      aria-describedby="birth-date-help birth-date-error"
    >
    <div id="birth-date-help" class="field-help">
      Legalább 18 évesnek kell lennie a regisztrációhoz
    </div>
    <div id="birth-date-error" class="error-message" role="alert" style="display: none;"></div>
  </div>
  
  <div class="form-group">
    <fieldset>
      <legend>Értesítési beállítások</legend>
      <div class="checkbox-group">
        <input type="checkbox" id="newsletter" name="newsletter" value="yes">
        <label for="newsletter">Szeretnék hírlevelet kapni</label>
      </div>
      <div class="checkbox-group">
        <input 
          type="checkbox" 
          id="terms" 
          name="terms" 
          required
          aria-describedby="terms-error"
        >
        <label for="terms">
          Elfogadom a <a href="/felhasznalasi-feltetelek" target="_blank">felhasználási feltételeket</a> *
        </label>
      </div>
      <div id="terms-error" class="error-message" role="alert" style="display: none;"></div>
    </fieldset>
  </div>
  
  <div class="form-actions">
    <button type="submit" class="btn-submit">Fiók létrehozása</button>
    <button type="reset" class="btn-reset">Űrlap törlése</button>
  </div>
</form>

<script>
document.addEventListener('DOMContentLoaded', function() {
  const form = document.querySelector('.registration-form');
  const inputs = form.querySelectorAll('input[required]');
  
  // Valós idejű validáció (opcionális)
  inputs.forEach(input => {
    input.addEventListener('blur', function() {
      validateField(input);
    });
    
    // Jelszó megerősítés speciális kezelése
    if (input.id === 'password-confirm') {
      input.addEventListener('input', function() {
        validatePasswordMatch();
      });
    }
  });
  
  // Űrlap beküldési validáció
  form.addEventListener('submit', function(event) {
    event.preventDefault();
    
    let isValid = true;
    const errors = [];
    
    // Minden kötelező mező ellenőrzése
    inputs.forEach(input => {
      const fieldValid = validateField(input);
      if (!fieldValid) {
        isValid = false;
        const label = form.querySelector(`label[for="${input.id}"]`);
        const fieldName = label ? label.textContent.replace(' *', '') : input.name;
        errors.push({
          field: input.id,
          fieldName: fieldName,
          message: getErrorMessage(input)
        });
      }
    });
    
    // Jelszó egyezés ellenőrzése
    if (!validatePasswordMatch()) {
      isValid = false;
    }
    
    if (isValid) {
      // Sikeres validáció - űrlap elküldése
      submitForm();
    } else {
      // Hibák megjelenítése
      displayFormErrors(errors);
      
      // Fókusz az első hibás mezőre
      const firstErrorField = form.querySelector('input[aria-invalid="true"]');
      if (firstErrorField) {
        firstErrorField.focus();
        firstErrorField.scrollIntoView({ behavior: 'smooth', block: 'center' });
      }
    }
  });
  
  function validateField(input) {
    const errorElement = document.getElementById(input.id + '-error');
    let isValid = true;
    let errorMessage = '';
    
    // Kötelező mező ellenőrzése
    if (input.hasAttribute('required') && !input.value.trim()) {
      isValid = false;
      errorMessage = `A ${getFieldLabel(input)} megadása kötelező.`;
    }
    // E-mail formátum ellenőrzése
    else if (input.type === 'email' && input.value && !isValidEmail(input.value)) {
      isValid = false;
      errorMessage = 'Kérjük, adjon meg egy érvényes e-mail címet (pl. nev@domain.hu).';
    }
    // Jelszó erősség ellenőrzése
    else if (input.id === 'password' && input.value && !isStrongPassword(input.value)) {
      isValid = false;
      errorMessage = 'A jelszónak legalább 8 karakter hosszúnak kell lennie, és tartalmaznia kell legalább egy számot és egy nagybetűt.';
    }
    // Felhasználónév formátum ellenőrzése
    else if (input.id === 'username' && input.value && !isValidUsername(input.value)) {
      isValid = false;
      if (input.value.length < 3) {
        errorMessage = 'A felhasználónév legalább 3 karakter hosszú legyen.';
      } else if (input.value.length > 20) {
        errorMessage = 'A felhasználónév legfeljebb 20 karakter hosszú lehet.';
      } else {
        errorMessage = 'A felhasználónév csak betűket és számokat tartalmazhat.';
      }
    }
    // Születési dátum ellenőrzése
    else if (input.id === 'birth-date' && input.value && !isValidAge(input.value)) {
      isValid = false;
      errorMessage = 'Legalább 18 évesnek kell lennie a regisztrációhoz.';
    }
    // Checkbox ellenőrzése
    else if (input.type === 'checkbox' && input.hasAttribute('required') && !input.checked) {
      isValid = false;
      errorMessage = 'El kell fogadnia a felhasználási feltételeket a folytatáshoz.';
    }
    
    // Hibaállapot beállítása
    input.setAttribute('aria-invalid', isValid ? 'false' : 'true');
    
    if (isValid) {
      errorElement.style.display = 'none';
      errorElement.textContent = '';
      input.classList.remove('field-error');
    } else {
      errorElement.style.display = 'block';
      errorElement.textContent = errorMessage;
      input.classList.add('field-error');
    }
    
    return isValid;
  }
  
  function validatePasswordMatch() {
    const password = document.getElementById('password');
    const passwordConfirm = document.getElementById('password-confirm');
    const errorElement = document.getElementById('password-confirm-error');
    
    if (passwordConfirm.value && password.value !== passwordConfirm.value) {
      passwordConfirm.setAttribute('aria-invalid', 'true');
      passwordConfirm.classList.add('field-error');
      errorElement.style.display = 'block';
      errorElement.textContent = 'A két jelszó nem egyezik meg.';
      return false;
    } else if (passwordConfirm.value) {
      passwordConfirm.setAttribute('aria-invalid', 'false');
      passwordConfirm.classList.remove('field-error');
      errorElement.style.display = 'none';
      errorElement.textContent = '';
    }
    
    return true;
  }
  
  function displayFormErrors(errors) {
    const formErrors = document.getElementById('form-errors');
    const errorList = document.getElementById('error-list');
    
    if (errors.length > 0) {
      errorList.innerHTML = '';
      errors.forEach(error => {
        const li = document.createElement('li');
        const link = document.createElement('a');
        link.href = `#${error.field}`;
        link.textContent = `${error.fieldName}: ${error.message}`;
        link.addEventListener('click', function(e) {
          e.preventDefault();
          document.getElementById(error.field).focus();
        });
        li.appendChild(link);
        errorList.appendChild(li);
      });
      
      formErrors.style.display = 'block';
      formErrors.scrollIntoView({ behavior: 'smooth', block: 'center' });
      
      // Képernyőolvasó értesítése
      announceToScreenReader(`${errors.length} hibát találtunk az űrlapban. Kérjük, javítsa ki őket.`);
    } else {
      formErrors.style.display = 'none';
    }
  }
  
  function getFieldLabel(input) {
    const label = form.querySelector(`label[for="${input.id}"]`);
    return label ? label.textContent.replace(' *', '').toLowerCase() : input.name;
  }
  
  function getErrorMessage(input) {
    const errorElement = document.getElementById(input.id + '-error');
    return errorElement ? errorElement.textContent : 'Érvénytelen érték.';
  }
  
  function isValidEmail(email) {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return emailRegex.test(email);
  }
  
  function isStrongPassword(password) {
    return password.length >= 8 && 
           /[A-Z]/.test(password) && 
           /[0-9]/.test(password);
  }
  
  function isValidUsername(username) {
    return username.length >= 3 && 
           username.length <= 20 && 
           /^[a-zA-Z0-9]+$/.test(username);
  }
  
  function isValidAge(birthDate) {
    const today = new Date();
    const birth = new Date(birthDate);
    const age = today.getFullYear() - birth.getFullYear();
    const monthDiff = today.getMonth() - birth.getMonth();
    
    if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())) {
      return age - 1 >= 18;
    }
    return age >= 18;
  }
  
  function submitForm() {
    announceToScreenReader('Űrlap sikeresen elküldve. Kérjük, várjon...');
    
    // Itt történne a tényleges űrlap elküldése
    console.log('Űrlap elküldése...');
    
    // Siker üzenet megjelenítése
    setTimeout(() => {
      alert('Regisztráció sikeres! Ellenőrizze e-mail címét az aktiválási linkért.');
    }, 1000);
  }
  
  function announceToScreenReader(message) {
    const announcement = document.createElement('div');
    announcement.setAttribute('role', 'status');
    announcement.setAttribute('aria-live', 'polite');
    announcement.className = 'visually-hidden';
    announcement.textContent = message;
    document.body.appendChild(announcement);
    
    setTimeout(() => {
      if (announcement.parentNode) {
        document.body.removeChild(announcement);
      }
    }, 1000);
  }
});
</script>

<style>
.registration-form {
  max-width: 600px;
  margin: 2rem auto;
  padding: 2rem;
  background: white;
  border: 1px solid #e9ecef;
  border-radius: 8px;
}

.form-section h2 {
  margin: 0 0 1.5rem 0;
  color: #495057;
}

.form-errors {
  background: #f8d7da;
  border: 1px solid #f5c6cb;
  border-radius: 4px;
  padding: 1rem;
  margin-bottom: 1.5rem;
}

.form-errors h3 {
  margin: 0 0 0.5rem 0;
  color: #721c24;
  font-size: 1.1em;
}

.form-errors ul {
  margin: 0;
  padding-left: 1.2rem;
}

.form-errors li {
  margin-bottom: 0.25rem;
}

.form-errors a {
  color: #721c24;
  text-decoration: none;
}

.form-errors a:hover, .form-errors a:focus {
  text-decoration: underline;
}

.form-group {
  margin-bottom: 1.5rem;
}

.form-group label {
  display: block;
  font-weight: 600;
  margin-bottom: 0.5rem;
  color: #495057;
}

.form-group input, .form-group select {
  width: 100%;
  padding: 0.75rem;
  border: 2px solid #ced4da;
  border-radius: 4px;
  font-size: 1rem;
  transition: border-color 0.2s ease;
}

.form-group input:focus, .form-group select:focus {
  outline: none;
  border-color: #007bff;
  box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1);
}

.form-group input.field-error {
  border-color: #dc3545;
  background-color: #fff5f5;
}

.form-group input.field-error:focus {
  border-color: #dc3545;
  box-shadow: 0 0 0 3px rgba(220, 53, 69, 0.1);
}

.field-help {
  margin-top: 0.5rem;
  font-size: 0.9em;
  color: #6c757d;
  line-height: 1.4;
}

.error-message {
  margin-top: 0.5rem;
  padding: 0.5rem;
  background: #f8d7da;
  border: 1px solid #f5c6cb;
  border-radius: 4px;
  color: #721c24;
  font-size: 0.9em;
  font-weight: 500;
}

fieldset {
  border: 1px solid #e9ecef;
  border-radius: 4px;
  padding: 1rem;
  margin: 0;
}

legend {
  font-weight: 600;
  color: #495057;
  padding: 0 0.5rem;
}

.checkbox-group {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
}

.checkbox-group input[type="checkbox"] {
  width: auto;
  margin-top: 0.25rem;
}

.checkbox-group label {
  margin-bottom: 0;
  font-weight: normal;
  cursor: pointer;
}

.form-actions {
  display: flex;
  gap: 1rem;
  margin-top: 2rem;
}

.btn-submit, .btn-reset {
  padding: 0.75rem 1.5rem;
  border: none;
  border-radius: 4px;
  font-size: 1rem;
  cursor: pointer;
  transition: all 0.2s ease;
}

.btn-submit {
  background: #28a745;
  color: white;
}

.btn-submit:hover, .btn-submit:focus {
  background: #218838;
}

.btn-reset {
  background: #6c757d;
  color: white;
}

.btn-reset:hover, .btn-reset:focus {
  background: #545b62;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

@media (max-width: 768px) {
  .registration-form {
    margin: 1rem;
    padding: 1rem;
  }
  
  .form-actions {
    flex-direction: column;
  }
}
</style>

Magyarázat: A hibaüzenetek az aria-describedby attribútummal kapcsolódnak a mezőkhöz, role=”alert” attribútummal dinamikusan bejelentésre kerülnek, és az aria-invalid attribútum jelzi a hibás állapotot a kisegítő technológiák számára.

2. Fókusz kezelése hiba után és programozott hibaazonosítás

<!-- KAPCSOLATFELVÉTELI ŰRLAP - Fejlett hibakezeléssel -->
<form class="contact-form" novalidate>
  <div class="form-header">
    <h2>Kapcsolatfelvételi űrlap</h2>
    
    <!-- Hibák összesítő doboza -->
    <div id="error-summary" class="error-summary" role="alert" aria-labelledby="error-summary-title" style="display: none;">
      <h3 id="error-summary-title">Az űrlapban hibák találhatók</h3>
      <p>Kérjük, javítsa ki az alábbi hibákat a folytatáshoz:</p>
      <ul id="error-summary-list"></ul>
    </div>
  </div>
  
  <div class="form-content">
    <div class="form-group">
      <label for="contact-name">
        Teljes név 
        <span class="required-indicator" aria-label="kötelező">*</span>
      </label>
      <input 
        type="text" 
        id="contact-name" 
        name="name" 
        required 
        minlength="2"
        aria-describedby="contact-name-help contact-name-error"
        autocomplete="name"
      >
      <div id="contact-name-help" class="field-help">
        Adja meg teljes nevét (vezetéknév és keresztnév)
      </div>
      <div id="contact-name-error" class="error-message" role="alert" style="display: none;"></div>
    </div>
    
    <div class="form-group">
      <label for="contact-email">
        E-mail cím 
        <span class="required-indicator" aria-label="kötelező">*</span>
      </label>
      <input 
        type="email" 
        id="contact-email" 
        name="email" 
        required
        aria-describedby="contact-email-help contact-email-error"
        autocomplete="email"
      >
      <div id="contact-email-help" class="field-help">
        Érvényes e-mail címet adjon meg (pl. nev@domain.hu)
      </div>
      <div id="contact-email-error" class="error-message" role="alert" style="display: none;"></div>
    </div>
    
    <div class="form-group">
      <label for="contact-phone">Telefonszám</label>
      <input 
        type="tel" 
        id="contact-phone" 
        name="phone"
        pattern="[\+]?[0-9\s\-\(\)]+"
        aria-describedby="contact-phone-help contact-phone-error"
        autocomplete="tel"
      >
      <div id="contact-phone-help" class="field-help">
        Opcionális. Magyar vagy nemzetközi formátumban (pl. +36 1 234 5678)
      </div>
      <div id="contact-phone-error" class="error-message" role="alert" style="display: none;"></div>
    </div>
    
    <div class="form-group">
      <label for="contact-subject">
        Tárgy 
        <span class="required-indicator" aria-label="kötelező">*</span>
      </label>
      <select id="contact-subject" name="subject" required aria-describedby="contact-subject-help contact-subject-error">
        <option value="">Válasszon témát</option>
        <option value="general">Általános kérdés</option>
        <option value="technical">Technikai probléma</option>
        <option value="billing">Számlázási kérdés</option>
        <option value="complaint">Panasz</option>
        <option value="suggestion">Javaslat</option>
      </select>
      <div id="contact-subject-help" class="field-help">
        Válassza ki a legmegfelelőbb kategóriát
      </div>
      <div id="contact-subject-error" class="error-message" role="alert" style="display: none;"></div>
    </div>
    
    <div class="form-group">
      <label for="contact-message">
        Üzenet 
        <span class="required-indicator" aria-label="kötelező">*</span>
      </label>
      <textarea 
        id="contact-message" 
        name="message" 
        required
        minlength="10"
        maxlength="1000"
        rows="5"
        aria-describedby="contact-message-help contact-message-error contact-message-count"
      ></textarea>
      <div id="contact-message-help" class="field-help">
        Részletesen írja le kérését vagy problémáját (minimum 10, maximum 1000 karakter)
      </div>
      <div id="contact-message-count" class="character-count" aria-live="polite">
        0 / 1000 karakter
      </div>
      <div id="contact-message-error" class="error-message" role="alert" style="display: none;"></div>
    </div>
    
    <div class="form-group">
      <fieldset>
        <legend>Adatvédelmi hozzájárulás</legend>
        <div class="checkbox-group">
          <input 
            type="checkbox" 
            id="privacy-consent" 
            name="privacy" 
            required
            aria-describedby="privacy-consent-error"
          >
          <label for="privacy-consent">
            Elolvastam és elfogadom az 
            <a href="/adatvedelem" target="_blank" rel="noopener">adatvédelmi tájékoztatót</a>
            <span class="required-indicator" aria-label="kötelező">*</span>
          </label>
        </div>
        <div id="privacy-consent-error" class="error-message" role="alert" style="display: none;"></div>
      </fieldset>
    </div>
    
    <div class="form-actions">
      <button type="submit" class="btn-submit">
        Üzenet küldése
        <span id="submit-status" class="visually-hidden" aria-live="polite"></span>
      </button>
      <button type="reset" class="btn-reset">Mezők törlése</button>
    </div>
  </div>
</form>

<script>
document.addEventListener('DOMContentLoaded', function() {
  const form = document.querySelector('.contact-form');
  const messageTextarea = document.getElementById('contact-message');
  const characterCount = document.getElementById('contact-message-count');
  const submitButton = form.querySelector('.btn-submit');
  const submitStatus = document.getElementById('submit-status');
  
  let isSubmitting = false;
  
  // Karakter számláló frissítése
  messageTextarea.addEventListener('input', function() {
    const length = this.value.length;
    const maxLength = 1000;
    characterCount.textContent = `${length} / ${maxLength} karakter`;
    
    if (length > maxLength * 0.9) {
      characterCount.classList.add('warning');
    } else {
      characterCount.classList.remove('warning');
    }
  });
  
  // Valós idejű validáció
  const inputs = form.querySelectorAll('input, select, textarea');
  inputs.forEach(input => {
    input.addEventListener('blur', function() {
      validateField(this);
    });
    
    // Azonnali visszajelzés javítás után
    input.addEventListener('input', function() {
      if (this.getAttribute('aria-invalid') === 'true') {
        clearFieldError(this);
      }
    });
  });
  
  // Űrlap beküldés kezelése
  form.addEventListener('submit', function(event) {
    event.preventDefault();
    
    if (isSubmitting) return;
    
    const validationResult = validateForm();
    
    if (validationResult.isValid) {
      submitForm();
    } else {
      handleFormErrors(validationResult.errors);
    }
  });
  
  function validateForm() {
    const errors = [];
    let isValid = true;
    
    inputs.forEach(input => {
      const fieldValid = validateField(input);
      if (!fieldValid.isValid) {
        isValid = false;
        errors.push({
          field: input.id,
          fieldName: getFieldLabel(input),
          message: fieldValid.message,
          element: input
        });
      }
    });
    
    return { isValid, errors };
  }
  
  function validateField(input) {
    const errorElement = document.getElementById(input.id + '-error');
    let isValid = true;
    let message = '';
    
    // Kötelező mezők ellenőrzése
    if (input.hasAttribute('required')) {
      if (input.type === 'checkbox' && !input.checked) {
        isValid = false;
        message = 'Az adatvédelmi tájékoztató elfogadása kötelező.';
      } else if (input.type !== 'checkbox' && !input.value.trim()) {
        isValid = false;
        message = `A ${getFieldLabel(input).toLowerCase()} megadása kötelező.`;
      }
    }
    
    // Specifikus validációk
    if (isValid && input.value.trim()) {
      switch (input.type) {
        case 'email':
          if (!isValidEmail(input.value)) {
            isValid = false;
            message = 'Kérjük, adjon meg egy érvényes e-mail címet.';
          }
          break;
          
        case 'tel':
          if (!isValidPhone(input.value)) {
            isValid = false;
            message = 'Kérjük, adjon meg egy érvényes telefonszámot.';
          }
          break;
      }
      
      // Hossz ellenőrzése
      if (input.hasAttribute('minlength') && input.value.length < parseInt(input.getAttribute('minlength'))) {
        isValid = false;
        const minLength = input.getAttribute('minlength');
        message = `Legalább ${minLength} karakter szükséges.`;
      }
      
      if (input.hasAttribute('maxlength') && input.value.length > parseInt(input.getAttribute('maxlength'))) {
        isValid = false;
        const maxLength = input.getAttribute('maxlength');
        message = `Maximum ${maxLength} karakter engedélyezett.`;
      }
    }
    
    // Select mező specifikus ellenőrzés
    if (input.tagName === 'SELECT' && input.hasAttribute('required') && !input.value) {
      isValid = false;
      message = 'Kérjük, válasszon egy opciót.';
    }
    
    // Hibaállapot beállítása
    updateFieldError(input, isValid, message);
    
    return { isValid, message };
  }
  
  function updateFieldError(input, isValid, message) {
    const errorElement = document.getElementById(input.id + '-error');
    
    input.setAttribute('aria-invalid', isValid ? 'false' : 'true');
    
    if (isValid) {
      input.classList.remove('field-error');
      if (errorElement) {
        errorElement.style.display = 'none';
        errorElement.textContent = '';
      }
    } else {
      input.classList.add('field-error');
      if (errorElement) {
        errorElement.style.display = 'block';
        errorElement.textContent = message;
      }
    }
  }
  
  function clearFieldError(input) {
    updateFieldError(input, true, '');
  }
  
  function handleFormErrors(errors) {
    // Hibák összesítő megjelenítése
    displayErrorSummary(errors);
    
    // Fókusz az első hibás mezőre
    if (errors.length > 0) {
      const firstErrorField = errors[0].element;
      
      // Smooth scroll az első hibához
      firstErrorField.scrollIntoView({ 
        behavior: 'smooth', 
        block: 'center' 
      });
      
      // Fókusz beállítása kis késleltetéssel
      setTimeout(() => {
        firstErrorField.focus();
      }, 300);
      
      // Képernyőolvasó értesítése
      announceToScreenReader(`${errors.length} hibát találtunk az űrlapban. A kurzor az első hibás mezőre került.`);
    }
  }
  
  function displayErrorSummary(errors) {
    const errorSummary = document.getElementById('error-summary');
    const errorList = document.getElementById('error-summary-list');
    
    if (errors.length > 0) {
      errorList.innerHTML = '';
      
      errors.forEach(error => {
        const li = document.createElement('li');
        const link = document.createElement('a');
        
        link.href = `#${error.field}`;
        link.textContent = `${error.fieldName}: ${error.message}`;
        link.addEventListener('click', function(e) {
          e.preventDefault();
          error.element.focus();
          error.element.scrollIntoView({ behavior: 'smooth', block: 'center' });
        });
        
        li.appendChild(link);
        errorList.appendChild(li);
      });
      
      errorSummary.style.display = 'block';
      errorSummary.scrollIntoView({ behavior: 'smooth', block: 'start' });
      
      // Fókusz az error summary-ra
      setTimeout(() => {
        errorSummary.focus();
      }, 300);
    } else {
      errorSummary.style.display = 'none';
    }
  }
  
  function submitForm() {
    isSubmitting = true;
    submitButton.disabled = true;
    submitStatus.textContent = 'Üzenet küldése folyamatban...';
    
    // Szimuláció: űrlap küldése
    setTimeout(() => {
      isSubmitting = false;
      submitButton.disabled = false;
      submitStatus.textContent = '';
      
      // Sikeres küldés
      announceToScreenReader('Üzenet sikeresen elküldve!');
      
      // Success üzenet megjelenítése
      const successMessage = document.createElement('div');
      successMessage.className = 'success-message';
      successMessage.setAttribute('role', 'alert');
      successMessage.innerHTML = `
        <h3>Üzenet sikeresen elküldve!</h3>
        <p>Köszönjük megkeresését. Munkatársunk 24 órán belül válaszol.</p>
      `;
      
      form.parentNode.insertBefore(successMessage, form);
      form.style.display = 'none';
      
      successMessage.scrollIntoView({ behavior: 'smooth', block: 'center' });
      successMessage.focus();
      
    }, 2000);
  }
  
  function getFieldLabel(input) {
    const label = form.querySelector(`label[for="${input.id}"]`);
    if (label) {
      return label.textContent.replace(/\s*\*\s*$/, '').trim();
    }
    return input.name || input.id;
  }
  
  function isValidEmail(email) {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return emailRegex.test(email);
  }
  
  function isValidPhone(phone) {
    const phoneRegex = /^[\+]?[0-9\s\-\(\)]{6,}$/;
    return phoneRegex.test(phone);
  }
  
  function announceToScreenReader(message) {
    const announcement = document.createElement('div');
    announcement.setAttribute('role', 'status');
    announcement.setAttribute('aria-live', 'polite');
    announcement.className = 'visually-hidden';
    announcement.textContent = message;
    document.body.appendChild(announcement);
    
    setTimeout(() => {
      if (announcement.parentNode) {
        document.body.removeChild(announcement);
      }
    }, 1000);
  }
});
</script>

<style>
.contact-form {
  max-width: 700px;
  margin: 2rem auto;
  padding: 2rem;
  background: white;
  border: 1px solid #e9ecef;
  border-radius: 8px;
}

.form-header h2 {
  margin: 0 0 1.5rem 0;
  color: #495057;
}

.error-summary {
  background: #f8d7da;
  border: 2px solid #f5c6cb;
  border-radius: 6px;
  padding: 1.5rem;
  margin-bottom: 2rem;
  outline: none;
}

.error-summary:focus {
  outline: 3px solid #dc3545;
  outline-offset: 2px;
}

.error-summary h3 {
  margin: 0 0 0.5rem 0;
  color: #721c24;
  font-size: 1.2em;
}

.error-summary p {
  margin: 0.5rem 0;
  color: #721c24;
}

.error-summary ul {
  margin: 0.5rem 0 0 0;
  padding-left: 1.5rem;
}

.error-summary li {
  margin-bottom: 0.5rem;
}

.error-summary a {
  color: #721c24;
  font-weight: 500;
  text-decoration: none;
}

.error-summary a:hover, .error-summary a:focus {
  text-decoration: underline;
  outline: 2px solid #721c24;
  outline-offset: 1px;
}

.required-indicator {
  color: #dc3545;
  font-weight: bold;
}

.character-count {
  font-size: 0.9em;
  color: #6c757d;
  text-align: right;
  margin-top: 0.25rem;
}

.character-count.warning {
  color: #856404;
  font-weight: bold;
}

.success-message {
  background: #d4edda;
  border: 2px solid #c3e6cb;
  border-radius: 6px;
  padding: 1.5rem;
  margin-bottom: 2rem;
  outline: none;
}

.success-message:focus {
  outline: 3px solid #28a745;
  outline-offset: 2px;
}

.success-message h3 {
  margin: 0 0 0.5rem 0;
  color: #155724;
}

.success-message p {
  margin: 0;
  color: #155724;
}

/* További stílusok az előző példából... */
</style>

Magyarázat: A rendszer automatikusan az első hibás mezőre irányítja a fókuszt, hibák összesítőt jelenít meg, és programozott kapcsolatot biztosít az aria-invalid és aria-describedby attribútumokkal.

3. Világos és konkrét hibaüzenetek leírásokkal

<!-- BEJELENTKEZÉSI ŰRLAP - Specifikus hibaüzenetekkel -->
<form class="login-form" novalidate>
  <div class="login-header">
    <h2>Bejelentkezés</h2>
    <p>Adja meg felhasználónevét és jelszavát a belépéshez.</p>
  </div>
  
  <div class="form-group">
    <label for="login-username">Felhasználónév vagy e-mail cím</label>
    <input 
      type="text" 
      id="login-username" 
      name="username" 
      required
      autocomplete="username"
      aria-describedby="login-username-error"
    >
    <div id="login-username-error" class="error-message" role="alert" style="display: none;"></div>
  </div>
  
  <div class="form-group">
    <label for="login-password">Jelszó</label>
    <div class="password-container">
      <input 
        type="password" 
        id="login-password" 
        name="password" 
        required
        autocomplete="current-password"
        aria-describedby="login-password-error"
      >
      <button 
        type="button" 
        class="password-toggle"
        aria-label="Jelszó megjelenítése"
        onclick="togglePasswordVisibility('login-password')"
      >
        <span class="toggle-text">Mutat</span>
      </button>
    </div>
    <div id="login-password-error" class="error-message" role="alert" style="display: none;"></div>
  </div>
  
  <div class="form-group">
    <div class="checkbox-group">
      <input type="checkbox" id="remember-me" name="remember">
      <label for="remember-me">Emlékezzen rám ezen az eszközön</label>
    </div>
  </div>
  
  <div class="form-actions">
    <button type="submit" class="btn-login">Bejelentkezés</button>
    <a href="/jelszo-visszaallitas" class="forgot-password">Elfelejtette jelszavát?</a>
  </div>
  
  <!-- Általános hiba üzenetek -->
  <div id="login-general-error" class="general-error" role="alert" style="display: none;"></div>
</form>

<!-- FIZETÉSI ŰRLAP - Komplex validációval -->
<form class="payment-form" novalidate>
  <div class="payment-header">
    <h2>Fizetési adatok</h2>
    <p>Minden mezőt pontosan töltsön ki a biztonságos fizetéshez.</p>
  </div>
  
  <div class="form-group">
    <label for="card-number">Bankkártya száma</label>
    <input 
      type="text" 
      id="card-number" 
      name="cardNumber" 
      required
      maxlength="19"
      pattern="[0-9\s]{13,19}"
      autocomplete="cc-number"
      aria-describedby="card-number-help card-number-error"
      placeholder="1234 5678 9012 3456"
    >
    <div id="card-number-help" class="field-help">
      16 számjegy, szóközökkel vagy anélkül
    </div>
    <div id="card-number-error" class="error-message" role="alert" style="display: none;"></div>
  </div>
  
  <div class="form-row">
    <div class="form-group form-group-half">
      <label for="expiry-date">Lejárati dátum</label>
      <input 
        type="text" 
        id="expiry-date" 
        name="expiryDate" 
        required
        maxlength="5"
        pattern="[0-9]{2}/[0-9]{2}"
        autocomplete="cc-exp"
        aria-describedby="expiry-date-help expiry-date-error"
        placeholder="MM/YY"
      >
      <div id="expiry-date-help" class="field-help">
        HH/ÉÉ formátumban
      </div>
      <div id="expiry-date-error" class="error-message" role="alert" style="display: none;"></div>
    </div>
    
    <div class="form-group form-group-half">
      <label for="cvv">CVC/CVV kód</label>
      <input 
        type="text" 
        id="cvv" 
        name="cvv" 
        required
        maxlength="4"
        pattern="[0-9]{3,4}"
        autocomplete="cc-csc"
        aria-describedby="cvv-help cvv-error"
        placeholder="123"
      >
      <div id="cvv-help" class="field-help">
        3-4 számjegy a kártya hátulján
      </div>
      <div id="cvv-error" class="error-message" role="alert" style="display: none;"></div>
    </div>
  </div>
  
  <div class="form-group">
    <label for="cardholder-name">Kártyatulajdonos neve</label>
    <input 
      type="text" 
      id="cardholder-name" 
      name="cardholderName" 
      required
      autocomplete="cc-name"
      aria-describedby="cardholder-name-help cardholder-name-error"
      placeholder="Ahogy a kártyán szerepel"
    >
    <div id="cardholder-name-help" class="field-help">
      Pontosan úgy, ahogy a bankkártyán szerepel
    </div>
    <div id="cardholder-name-error" class="error-message" role="alert" style="display: none;"></div>
  </div>
  
  <div class="form-actions">
    <button type="submit" class="btn-pay">
      Fizetés: <span class="amount">15.990 Ft</span>
    </button>
  </div>
</form>

<script>
// Specifikus hibaüzenetek definiálása
const ErrorMessages = {
  // Bejelentkezési hibák
  login: {
    usernameRequired: 'Kérjük, adja meg felhasználónevét vagy e-mail címét.',
    usernameInvalid: 'A felhasználónév vagy e-mail cím formátuma helytelen.',
    passwordRequired: 'Kérjük, adja meg jelszavát.',
    passwordTooShort: 'A jelszó túl rövid. Legalább 6 karakter szükséges.',
    invalidCredentials: 'Hibás felhasználónév vagy jelszó. Kérjük, ellenőrizze adatait és próbálja újra.',
    accountLocked: 'A fiók átmenetileg zárolva van túl sok sikertelen bejelentkezési kísérlet miatt. Próbálja újra 15 perc múlva.',
    accountDisabled: 'Ez a fiók inaktív. Kérjük, vegye fel a kapcsolatot az ügyfélszolgálattal.',
  },
  
  // Fizetési hibák
  payment: {
    cardNumberRequired: 'Kérjük, adja meg bankkártyájának számát.',
    cardNumberInvalid: 'A bankkártya száma érvénytelen. Kérjük, ellenőrizze és adja meg újra (16 számjegy).',
    cardNumberUnsupported: 'Ez a kártyatípus nem támogatott. Kérjük, használjon Visa vagy Mastercard kártyát.',
    expiryRequired: 'Kérjük, adja meg a kártya lejárati dátumát.',
    expiryInvalid: 'A lejárati dátum formátuma helytelen. Használja a HH/ÉÉ formátumot (pl. 12/25).',
    expiryExpired: 'Ez a bankkártya már lejárt. Kérjük, használjon érvényes kártyát.',
    cvvRequired: 'Kérjük, adja meg a CVC/CVV kódot.',
    cvvInvalid: 'A CVC/CVV kód érvénytelen. 3-4 számjegyű kód szükséges.',
    cardholderRequired: 'Kérjük, adja meg a kártyatulajdonos nevét.',
    cardholderInvalid: 'A kártyatulajdonos neve csak betűket és szóközöket tartalmazhat.',
    processingError: 'Hiba történt a fizetés feldolgozása során. Kérjük, próbálja újra.'
  }
};

// Bejelentkezési űrlap kezelése
document.querySelector('.login-form').addEventListener('submit', function(event) {
  event.preventDefault();
  
  const username = document.getElementById('login-username');
  const password = document.getElementById('login-password');
  const generalError = document.getElementById('login-general-error');
  
  let hasErrors = false;
  
  // Felhasználónév validáció
  if (!username.value.trim()) {
    showFieldError('login-username', ErrorMessages.login.usernameRequired);
    hasErrors = true;
  } else if (!isValidUsernameOrEmail(username.value)) {
    showFieldError('login-username', ErrorMessages.login.usernameInvalid);
    hasErrors = true;
  } else {
    clearFieldError('login-username');
  }
  
  // Jelszó validáció
  if (!password.value) {
    showFieldError('login-password', ErrorMessages.login.passwordRequired);
    hasErrors = true;
  } else if (password.value.length < 6) {
    showFieldError('login-password', ErrorMessages.login.passwordTooShort);
    hasErrors = true;
  } else {
    clearFieldError('login-password');
  }
  
  if (hasErrors) {
    // Fókusz az első hibás mezőre
    const firstError = this.querySelector('input[aria-invalid="true"]');
    if (firstError) {
      firstError.focus();
    }
    return;
  }
  
  // Szimulált bejelentkezési kísérlet
  simulateLogin(username.value, password.value, generalError);
});

// Fizetési űrlap kezelése
document.querySelector('.payment-form').addEventListener('submit', function(event) {
  event.preventDefault();
  
  const fields = {
    cardNumber: document.getElementById('card-number'),
    expiryDate: document.getElementById('expiry-date'),
    cvv: document.getElementById('cvv'),
    cardholderName: document.getElementById('cardholder-name')
  };
  
  let hasErrors = false;
  
  // Bankkártya szám validáció
  const cardNumber = fields.cardNumber.value.replace(/\s/g, '');
  if (!cardNumber) {
    showFieldError('card-number', ErrorMessages.payment.cardNumberRequired);
    hasErrors = true;
  } else if (!isValidCardNumber(cardNumber)) {
    showFieldError('card-number', ErrorMessages.payment.cardNumberInvalid);
    hasErrors = true;
  } else if (!isSupportedCardType(cardNumber)) {
    showFieldError('card-number', ErrorMessages.payment.cardNumberUnsupported);
    hasErrors = true;
  } else {
    clearFieldError('card-number');
  }
  
  // Lejárati dátum validáció
  const expiry = fields.expiryDate.value;
  if (!expiry) {
    showFieldError('expiry-date', ErrorMessages.payment.expiryRequired);
    hasErrors = true;
  } else if (!isValidExpiryFormat(expiry)) {
    showFieldError('expiry-date', ErrorMessages.payment.expiryInvalid);
    hasErrors = true;
  } else if (isCardExpired(expiry)) {
    showFieldError('expiry-date', ErrorMessages.payment.expiryExpired);
    hasErrors = true;
  } else {
    clearFieldError('expiry-date');
  }
  
  // CVV validáció
  const cvv = fields.cvv.value;
  if (!cvv) {
    showFieldError('cvv', ErrorMessages.payment.cvvRequired);
    hasErrors = true;
  } else if (!isValidCVV(cvv)) {
    showFieldError('cvv', ErrorMessages.payment.cvvInvalid);
    hasErrors = true;
  } else {
    clearFieldError('cvv');
  }
  
  // Kártyatulajdonos validáció
  const cardholderName = fields.cardholderName.value.trim();
  if (!cardholderName) {
    showFieldError('cardholder-name', ErrorMessages.payment.cardholderRequired);
    hasErrors = true;
  } else if (!isValidCardholderName(cardholderName)) {
    showFieldError('cardholder-name', ErrorMessages.payment.cardholderInvalid);
    hasErrors = true;
  } else {
    clearFieldError('cardholder-name');
  }
  
  if (hasErrors) {
    const firstError = this.querySelector('input[aria-invalid="true"]');
    if (firstError) {
      firstError.focus();
    }
    return;
  }
  
  // Fizetés feldolgozása
  processPayment();
});

// Segédfüggvények
function showFieldError(fieldId, message) {
  const field = document.getElementById(fieldId);
  const errorElement = document.getElementById(fieldId + '-error');
  
  field.setAttribute('aria-invalid', 'true');
  field.classList.add('field-error');
  errorElement.style.display = 'block';
  errorElement.textContent = message;
}

function clearFieldError(fieldId) {
  const field = document.getElementById(fieldId);
  const errorElement = document.getElementById(fieldId + '-error');
  
  field.setAttribute('aria-invalid', 'false');
  field.classList.remove('field-error');
  errorElement.style.display = 'none';
  errorElement.textContent = '';
}

function isValidUsernameOrEmail(value) {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  const usernameRegex = /^[a-zA-Z0-9_]{3,}$/;
  return emailRegex.test(value) || usernameRegex.test(value);
}

function isValidCardNumber(cardNumber) {
  return cardNumber.length === 16 && /^[0-9]+$/.test(cardNumber);
}

function isSupportedCardType(cardNumber) {
  // Visa: 4-gyel kezdődik, Mastercard: 5-tel kezdődik
  return cardNumber.startsWith('4') || cardNumber.startsWith('5');
}

function isValidExpiryFormat(expiry) {
  return /^[0-9]{2}\/[0-9]{2}$/.test(expiry);
}

function isCardExpired(expiry) {
  const [month, year] = expiry.split('/').map(Number);
  const now = new Date();
  const currentYear = now.getFullYear() % 100;
  const currentMonth = now.getMonth() + 1;
  
  return year < currentYear || (year === currentYear && month < currentMonth);
}

function isValidCVV(cvv) {
  return /^[0-9]{3,4}$/.test(cvv);
}

function isValidCardholderName(name) {
  return /^[a-zA-ZáéíóöőúüűÁÉÍÓÖŐÚÜŰ\s]{2,}$/.test(name);
}

function simulateLogin(username, password, generalErrorElement) {
  // Szimulált bejelentkezési logika
  setTimeout(() => {
    const randomOutcome = Math.random();
    
    if (randomOutcome < 0.3) {
      // Sikertelen bejelentkezés
      generalErrorElement.textContent = ErrorMessages.login.invalidCredentials;
      generalErrorElement.style.display = 'block';
    } else if (randomOutcome < 0.4) {
      // Zárolt fiók
      generalErrorElement.textContent = ErrorMessages.login.accountLocked;
      generalErrorElement.style.display = 'block';
    } else {
      // Sikeres bejelentkezés
      alert('Sikeres bejelentkezés!');
    }
  }, 1000);
}

function processPayment() {
  alert('Fizetés feldolgozása... (szimulált)');
}

function togglePasswordVisibility(fieldId) {
  const field = document.getElementById(fieldId);
  const button = field.nextElementSibling;
  const toggleText = button.querySelector('.toggle-text');
  
  if (field.type === 'password') {
    field.type = 'text';
    button.setAttribute('aria-label', 'Jelszó elrejtése');
    toggleText.textContent = 'Rejt';
  } else {
    field.type = 'password';
    button.setAttribute('aria-label', 'Jelszó megjelenítése');
    toggleText.textContent = 'Mutat';
  }
}
</script>

<style>
.login-form, .payment-form {
  max-width: 500px;
  margin: 2rem auto;
  padding: 2rem;
  background: white;
  border: 1px solid #e9ecef;
  border-radius: 8px;
}

.password-container {
  position: relative;
}

.password-toggle {
  position: absolute;
  right: 0.5rem;
  top: 50%;
  transform: translateY(-50%);
  background: #f8f9fa;
  border: 1px solid #ced4da;
  border-radius: 4px;
  padding: 0.25rem 0.5rem;
  cursor: pointer;
  font-size: 0.8em;
}

.form-row {
  display: flex;
  gap: 1rem;
}

.form-group-half {
  flex: 1;
}

.general-error {
  background: #f8d7da;
  border: 1px solid #f5c6cb;
  border-radius: 4px;
  padding: 1rem;
  margin-top: 1rem;
  color: #721c24;
}

.btn-login, .btn-pay {
  width: 100%;
  padding: 0.75rem;
  background: #007bff;
  color: white;
  border: none;
  border-radius: 4px;
  font-size: 1rem;
  cursor: pointer;
}

.btn-pay {
  background: #28a745;
  font-size: 1.1em;
  font-weight: bold;
}

.amount {
  background: rgba(255, 255, 255, 0.2);
  padding: 0.25rem 0.5rem;
  border-radius: 4px;
}

.forgot-password {
  display: block;
  text-align: center;
  margin-top: 1rem;
  color: #007bff;
  text-decoration: none;
}
</style>

Magyarázat: A hibaüzenetek konkrétak és konstruktívak, világosan megmondják, mi a probléma és hogyan javítható ki. Kerülik a homályos megfogalmazást, mint például az „Érvénytelen bevitel”.

Rossz megoldások

Nem kapcsolódnak hibaüzenetek a beviteli mezőkhöz

<!-- HIBÁS: A hibaüzenet nincs programozottan kapcsolva -->
<form>
  <label for="phone">Telefonszám:</label>
  <input id="phone" type="tel" required>
  <!-- HIBA: Nincs aria-describedby vagy más kapcsolat -->
  <span style="color: red;">Kérjük, adja meg telefonszámát.</span>
</form>

<!-- HIBÁS: Hibaüzenet távol a mezőtől -->
<form>
  <div class="form-group">
    <label for="email">E-mail:</label>
    <input id="email" type="email" required>
  </div>
  
  <div class="form-group">
    <label for="password">Jelszó:</label>
    <input id="password" type="password" required>
  </div>
  
  <!-- HIBA: Hibaüzenetek messze a mezőktől, nincs kapcsolat -->
  <div class="errors">
    <p>Az e-mail cím érvénytelen.</p>
    <p>A jelszó túl rövid.</p>
  </div>
</form>

Probléma: A képernyőolvasók nem tudják azonosítani, hogy melyik hibaüzenet, melyik mezőhöz tartozik, mert nincs programozott kapcsolat közöttük.

Csak szín használata hibák jelölésére

<!-- HIBÁS: Csak vizuális jelzés színnel -->
<form>
  <label for="zip">Irányítószám:</label>
  <!-- HIBA: Csak a piros szegély jelzi a hibát -->
  <input id="zip" type="text" style="border: 2px solid red;" required>
  <!-- Nincs szöveges hibaüzenet -->
</form>

<!-- HIBÁS: Ikonok szöveg nélkül -->
<form>
  <label for="username">Felhasználónév:</label>
  <div class="input-container">
    <input id="username" type="text" required>
    <!-- HIBA: Csak vizuális ikon, nincs szöveges információ -->
    <span class="error-icon" style="color: red;">❌</span>
  </div>
</form>

<!-- HIBÁS: Háttérszín változás szöveg nélkül -->
<form>
  <label for="age">Életkor:</label>
  <!-- HIBA: Csak a rózsaszín háttér jelzi a hibát -->
  <input id="age" type="number" style="background-color: #ffcccc;" required>
</form>

Probléma: A színvak vagy gyengénlátó felhasználók nem érzékelik a színváltozásokat, és nincs alternatív módja a hibák azonosításának.

Nem jelenik meg a hibaüzenet

<!-- HIBÁS: Néma validáció -->
<form>
  <label for="required-field">Kötelező mező:</label>
  <input id="required-field" type="text" required>
  <button type="submit">Küldés</button>
  
  <script>
    // HIBA: Csak console.log, nincs felhasználói visszajelzés
    document.querySelector('form').addEventListener('submit', function(e) {
      const input = document.getElementById('required-field');
      if (!input.value) {
        e.preventDefault();
        console.log('Hiba: Kötelező mező üres'); // Felhasználó nem látja!
      }
    });
  </script>
</form>

<!-- HIBÁS: Alert popup-ok -->
<form>
  <label for="email-bad">E-mail:</label>
  <input id="email-bad" type="email" required>
  <button type="submit">Küldés</button>
  
  <script>
    // HIBA: Alert popup nem akadálymentes és zavaró
    document.querySelector('form').addEventListener('submit', function(e) {
      const email = document.getElementById('email-bad');
      if (!email.value) {
        e.preventDefault();
        alert('E-mail cím megadása kötelező!'); // Rossz UX
      }
    });
  </script>
</form>

<!-- HIBÁS: Rejtett hibaüzenetek -->
<form>
  <label for="password-bad">Jelszó:</label>
  <input id="password-bad" type="password" required>
  <!-- HIBA: Hibaüzenet létezik, de sosem válik láthatóvá -->
  <div id="password-error" style="display: none; visibility: hidden;">
    Jelszó szükséges
  </div>
  <button type="submit">Küldés</button>
</form>

Probléma: A felhasználók nem kapnak visszajelzést a hibákról, így nem tudják, mit javítsanak ki.

A fókusz a küldés gombon marad hiba esetén

<!-- HIBÁS: Rossz fókusz kezelés -->
<form id="bad-focus-form">
  <div>
    <label for="name-bad">Név:</label>
    <input id="name-bad" type="text" required>
    <div id="name-bad-error" style="display: none; color: red;"></div>
  </div>
  
  <div>
    <label for="email-bad-focus">E-mail:</label>
    <input id="email-bad-focus" type="email" required>
    <div id="email-bad-focus-error" style="display: none; color: red;"></div>
  </div>
  
  <button type="submit" id="submit-bad">Küldés</button>
</form>

<script>
document.getElementById('bad-focus-form').addEventListener('submit', function(e) {
  e.preventDefault();
  
  const nameField = document.getElementById('name-bad');
  const emailField = document.getElementById('email-bad-focus');
  const submitButton = document.getElementById('submit-bad');
  
  let hasErrors = false;
  
  // Hibák ellenőrzése
  if (!nameField.value) {
    document.getElementById('name-bad-error').textContent = 'Név kötelező';
    document.getElementById('name-bad-error').style.display = 'block';
    hasErrors = true;
  }
  
  if (!emailField.value) {
    document.getElementById('email-bad-focus-error').textContent = 'E-mail kötelező';
    document.getElementById('email-bad-focus-error').style.display = 'block';
    hasErrors = true;
  }
  
  if (hasErrors) {
    // HIBA: Fókusz a küldés gombon marad
    submitButton.focus(); // Rossz! Nem segít a hibák megtalálásában
    
    // HIBA: Nincs scroll a hibákhoz
    // A felhasználó nem tudja, hol vannak a hibák
  }
});
</script>

<!-- HIBÁS: Nincs hibák összesítő -->
<form id="no-summary-form">
  <!-- Sok mező... -->
  <div>
    <label for="field1">Mező 1:</label>
    <input id="field1" type="text" required>
  </div>
  <div>
    <label for="field2">Mező 2:</label>
    <input id="field2" type="text" required>
  </div>
  <div>
    <label for="field3">Mező 3:</label>
    <input id="field3" type="text" required>
  </div>
  <div>
    <label for="field4">Mező 4:</label>
    <input id="field4" type="text" required>
  </div>
  <div>
    <label for="field5">Mező 5:</label>
    <input id="field5" type="text" required>
  </div>
  
  <button type="submit">Küldés</button>
  
  <!-- HIBA: Nincs hibák összesítő a tetején -->
  <!-- Hosszú űrlapnál a felhasználó nem látja az összes hibát -->
</form>

Probléma: A billentyűzetes felhasználók és képernyőolvasó használók nehezen találják meg és javítják ki a hibákat, ha a fókusz nem a hibás mezőre kerül.

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