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
- HTML Kısıtlaması: Tarayıcının yerleşik doğrulama mantığını tetiklemek için
type="email",patternverequiredgibi standart HTML5 özelliklerini KULLANIN. - Görsel Geri Bildirim: Hata stilini yalnızca etkileşimden sonra uygulamak için
:user-invalidkullanın. - 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-validkullanın. - Sorunsuz kurtarma: Kullanıcı girişi geçerli bir biçime düzelttiği anda
:user-invalideş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
- Erişilebilirlik:
- ZORUNLU: Kurallar listesini girişe bağlamak için
aria-describedbykullanın. - YAPMAYIN: Giriş geçerli olana kadar kural listelerini tamamen gizlemeyin. Kullanıcıların ne yazacaklarını bilmeleri gerekir.
- ZORUNLU: Kurallar listesini girişe bağlamak için
- 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. - Doğrulama Katılığı: Tarayıcının varsayılan
type="email"doğrulamasının oldukça esnek olduğunu (ör.user@localservergeçerli olabilir) unutmayın. Daha katı bir doğrulama gerekiyorsatype="email"ile birlikte daha güçlü bir doğrulama kitaplığı veya özel bir doğrulama işlevi kullanmanız gerekebilir. - 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-invalidstilleriniz hemen uygulanır. - 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);
});