Etkileşimden Sonra Girişi Doğrula

Web özellikleri: :user-valid ve :user-invalid

Sorun

Kullanıcı bir alana odaklanıp yazmaya başladığı anda doğrulama hatalarının gösterilmesi erken ve dikkat dağıtıcıdır. Örneğin, kullanıcı bir e-posta adresi (ör. "kullanici@gm") veya karmaşık gereksinimleri olan bir şifre yazarken alan, tamamlanana kadar teknik olarak geçersizdir. Standart :invalid stilinde hata durumu hemen gösterilir ve bu da kullanıcıyı rahatsız eder.

Çözüm

:user-invalid sözde sınıfı, kullanıcı bir değer "girene" (alanı bulanıklaştırarak) veya formu göndermeye çalışana kadar hata durumunu ertelemenize olanak tanır. Bu sayede, doğrulama geri bildirimi yalnızca kullanıcı alanla etkileşimi tamamladıktan sonra sağlanır.

Uygulama Stratejisi

  1. HTML Kısıtlaması: Tarayıcının yerleşik doğrulama mantığını tetiklemek için type="email", pattern ve required gibi standart HTML5 özelliklerini KULLANIN.
  2. Görsel Geri Bildirim: Hata stilini yalnızca etkileşimden sonra uygulamak için :user-invalid kullanın.
  3. Olumlu Pekiştirme: Koşullar karşılandığında yeşil bir "başarı" göstergesi göstermek için isteğe bağlı olarak :user-valid kullanın.
  4. Sorunsuz kurtarma: Kullanıcı girişi geçerli bir biçime düzelttiği anda :user-invalid eşleşmeyi durdurur ve hata durumunu hemen kaldırır.

Uygulama Kılavuzu

1. Kullanım Alanı: E-posta Doğrulaması

ZORUNLU: E-posta alanları için standart HTML5 özelliklerini kullanın. Hata mesajı varsayılan olarak gizlidir ve yalnızca tarayıcı, kullanıcının alanı geçersiz bir durumda bıraktığını belirlediğinde gösterilir.

<form>
  <div class="field">
    <label for="email">Email Address</label>
    <!-- MANDATORY: Place format hints above the input so autocomplete popovers don't cover them during editing -->
    <span id="email-hint" class="hint">Format: you@example.com</span>
    <!-- DO: Use standard HTML validation attributes like type="email" and required -->
    <input
      type="email"
      id="email"
      name="email"
      required
      autocomplete="email"
      aria-describedby="email-hint"
      aria-errormessage="email-error"
    >
    <div id="email-error" class="error-msg">
      <span aria-hidden="true">❌</span> Please enter a valid email address.
    </div>
  </div>
</form>
.hint {
  display: block;
  color: #5f6368;
  font-size: 0.85rem;
  margin-bottom: 0.25rem;
}

.error-msg {
  display: none;
  color: #d93025;
  font-size: 0.875rem;
  margin-top: 0.25rem;
}

/*
  DO: Only show error styles after user interaction.
  Use multiple indicators (border/background shift + icon/text) to avoid color-only states.
*/
input:user-invalid {
  border-color: #d93025;
  background-color: #fce8e6;
}

/* DO: Reveal the error message using the adjacent sibling selector */
input:user-invalid + .error-msg {
  display: block;
}

/* DO: Provide a clear success indication on :user-valid */
input:user-valid {
  border-color: #188038;
}

2. Kullanım Alanı: Şifre Karmaşıklığı

ZORUNLU: pattern özelliğinde bir Regex Lookahead kalıbı kullanarak karmaşıklık kuralını tanımlayın. Kullanıcıya yol göstermek için girişin üzerinde kurallar listesi gösterilir ve hata varsa vurgulanır.

<form>
  <div class="field">
    <label for="password">New Password</label>
    <!-- MANDATORY: Place hints and rules above the input so mobile keyboards do not obscure them -->
    <ul id="password-rules" class="rules-list">
      <li>At least 8 characters</li>
      <li>One uppercase letter</li>
      <li>One number</li>
      <li>One special character</li>
    </ul>
    <!-- DO: Use pattern and minlength for complex password validation
         DO: Match all constraints with lookaheads via pattern attribute
     -->
    <input
      type="password"
      id="password"
      autocomplete="new-password"
      required
      pattern="(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[\W_]).{8,}"
      minlength="8"
      aria-describedby="password-rules"
    >
  </div>
</form>
/* DO: State the default styling as neutral */
.rules-list { 
  color: #5f6368; 
  margin-bottom: 0.5rem;
}

/* DO: Show invalid state (After interaction): Error */
input:user-invalid {
  border-color: #d93025;
  background-color: #fce8e6;
}

/* DO: Highlight rules list when error is shown using the modern :has() selector */
.field:has(input:user-invalid) .rules-list {
  color: #d93025;
  font-weight: 600;
}

/* DO: Add success indications for :user-valid state */
input:user-valid {
  border-color: #188038;
}

/* DO: Hide rules once satisfied */
.field:has(input:user-valid) .rules-list {
  display: none;
}

Yedekleme ve Tarayıcı Desteği

:user-valid ve :user-invalid için yedekler ve tarayıcı desteği

Yedek için CSS

input:user-invalid,
input.user-invalid-fallback {
  border-color: #d93025;
  background-color: #fce8e6;
}

input:user-invalid + .error-msg,
input.user-invalid-fallback + .error-msg {
  display: block;
}

JavaScript Yedek

WeakMap kullanarak etkileşim durumunu izleyen yeniden kullanılabilir bir yardımcı program kullanın. Bu sayede DOM'un "kirli" sınıflar veya veri özellikleri ile kirlenmesi önlenir.

const UserInvalidFallback = (() => {
  const dirtyState = new WeakMap();

  const updateState = (input) => {
    const isValid = input.checkValidity();

    // Update both visual and ARIA state
    input.classList.toggle('user-invalid-fallback', !isValid);
    input.classList.toggle('user-valid-fallback', isValid);

    if (!isValid) {
      input.setAttribute('aria-invalid', 'true');
    } else {
      input.removeAttribute('aria-invalid');
    }
  };

  const handleEvent = (event) => {
    const input = event.target;

    if (event.type === 'reset') {
      const controls = input.elements || [];
      for (const control of controls) {
        dirtyState.delete(control);
        control.classList.remove('user-invalid-fallback');
        control.classList.remove('user-valid-fallback');
        control.removeAttribute('aria-invalid');
      }
      return;
    }

    if (!input.checkValidity) return;

    if (event.type === 'input' || event.type === 'change') {
      const state = dirtyState.get(input) || { hasInteracted: false, hasBlurred: false };
      state.hasInteracted = true;
      dirtyState.set(input, state);
      if (state.hasBlurred) {
        updateState(input);
      }
    } else if (event.type === 'blur') {
      const state = dirtyState.get(input) || { hasInteracted: false, hasBlurred: false };
      state.hasBlurred = true;
      dirtyState.set(input, state);
      if (state.hasInteracted) {
        updateState(input);
      }
    }
  };

  const init = (root = document) => {
    if (CSS.supports('selector(:user-invalid)')) return;

    root.addEventListener('blur', handleEvent, true); // Capture phase
    root.addEventListener('input', handleEvent);
    root.addEventListener('change', handleEvent);
    root.addEventListener('reset', handleEvent, true); // Capture resets
  };

  return { init };
})();

// Initialize for a specific form
const form = document.querySelector('#demo-form');
UserInvalidFallback.init(form);

Dikkat edilmesi gereken diğer noktalar

  1. Erişilebilirlik:
    • ZORUNLU: Kurallar listesini girişe bağlamak için aria-describedby kullanın.
    • YAPMAYIN: Giriş geçerli olana kadar kural listelerini tamamen gizlemeyin. Kullanıcıların ne yazacaklarını bilmeleri gerekir.
  2. Desen Özelliği Sınırları: ZORUNLU: pattern özelliği tam eşleşme gerçekleştirir (dolaylı ^...$). Şifre normal ifadenizin tüm dizeyi kapsadığından emin olun.
  3. Doğrulama Katılığı: Tarayıcının varsayılan type="email" doğrulamasının oldukça esnek olduğunu (ör. user@localserver geçerli olabilir) unutmayın. Daha katı bir doğrulama gerekiyorsa type="email" ile birlikte daha güçlü bir doğrulama kitaplığı veya özel bir doğrulama işlevi kullanmanız gerekebilir.
  4. Odak Yönetimi: ZORUNLU: Kullanıcı formu geçersiz bir alanla gönderirse tarayıcı otomatik olarak ilk geçersiz alana odaklanır. Gönderme denemesi bir etkileşim olarak sayıldığından :user-invalid stilleriniz hemen uygulanır.
  5. Tutarlı ARIA deneyimi: Yerel :user-invalid, ARIA özellikleriyle otomatik olarak senkronize edilmez. aria-invalid öğesini görsel durumla senkronize tutmak için aşağıdaki JavaScript'i ekleyin:
// Sync aria-invalid with the CSS :user-invalid state
const syncAria = (el) => {
  el.setAttribute?.('aria-invalid', el.matches(':user-invalid') ? 'true' : 'false');
};

// Update on blur (to show error) and input (to clear it)
document.addEventListener('blur', (e) => syncAria(e.target), true);
document.addEventListener('input', (e) => {
  if (e.target.hasAttribute('aria-invalid')) syncAria(e.target);
});