सुलभता से जुड़ी गड़बड़ी की सूचना

वेब की सुविधाएं: :user-valid और :user-invalid

समस्या

स्टैंडर्ड एचटीएमएल5 वैलिडेशन, विज़ुअल फ़ीडबैक (:invalid या :user-invalid के ज़रिए) देता है. हालांकि, यह aria-invalid जैसे सुलभता एट्रिब्यूट के साथ अपने-आप सिंक नहीं होता.

अगर स्टैंडर्ड :invalid स्टाइल का इस्तेमाल किया जाता है, तो स्क्रीन रीडर "अमान्य एंट्री" की सूचना दे सकते हैं. ऐसा तब होता है, जब कोई उपयोगकर्ता किसी ज़रूरी फ़ील्ड में टैब करता है, जो फ़िलहाल खाली है. इससे सहायक टेक्नोलॉजी का इस्तेमाल करने वाले लोगों को खराब अनुभव मिलता है. ऐसा इसलिए होता है, क्योंकि इंटरैक्शन होने से पहले ही गड़बड़ी की सूचना मिल जाती है.

समस्या का हल

हम चाहते हैं कि प्रोग्रामैटिक स्टेट (aria-invalid="true") को ठीक उसी समय लागू किया जाए जब विज़ुअल स्टेट (:user-invalid) लागू हो. :user-invalid, ब्राउज़र के अंदरूनी "user-interacted" फ़्लैग पर निर्भर करता है. इसलिए, हम JavaScript का इस्तेमाल करके यह देख सकते हैं कि स्टैंडर्ड इंटरैक्शन इवेंट के दौरान यह सिलेक्टर मैच करता है या नहीं.

ज़्यादा जानकारी के लिए, MDN aria-invalid देखें.

लागू करने की रणनीति

  1. विज़ुअल लेयर: बॉर्डर/आइकॉन दिखाने के लिए, सीएसएस :user-invalid का इस्तेमाल करें.
  2. सुलभता लेयर: सहायक टेक्नोलॉजी (एटी) को स्थिति की जानकारी देने के लिए, aria-invalid और aria-errormessage का इस्तेमाल करें.
  3. ब्रिज विज़ुअल और सुलभता लेयर: एक लाइटवेट JavaScript उपयोगिता बनाएं, जो blur और input इवेंट को लिसन करती हो. साथ ही, यह जांच करती हो कि एलिमेंट, :user-invalid से मैच करता है या नहीं. इसके बाद, ARIA एट्रिब्यूट को उसी हिसाब से अपडेट करती हो.

लागू करने के बारे में जानकारी

1. एचटीएमएल स्ट्रक्चर

aria-errormessage (या ज़्यादा सहायता के लिए aria-describedby) का इस्तेमाल करके, अपने इनपुट को गड़बड़ी के मैसेज से लिंक करें.

<form>
  <div class="field">
    <label for="email">Email</label>
    <input 
      type="email" 
      id="email" 
      required 
      aria-errormessage="email-error"
    >
    <span id="email-error" class="error-msg">
      Please enter a valid email address.
    </span>
  </div>
</form>

2. सीएसएस

नेटिव स्यूडो-क्लास :user-invalid का इस्तेमाल करके, गड़बड़ी के मैसेज को दिखने से रोकें.

.error-msg {
  display: none;
  color: #d93025;
}

/* Show error message when input is user-invalid */
input:user-invalid ~ .error-msg {
  display: block;
}

/* Optional: Visual cues on the input itself */
input:user-invalid {
  border-color: #d93025;
}

3. JavaScript

"UserInvalidChanged" इवेंट मौजूद नहीं है. इसलिए, स्थिति की जांच करने के लिए स्टैंडर्ड फ़ॉर्म इवेंट में हुक करें.

const updateAriaState = (event) => {
  const input = event.target;
  if (!input.matches?.('input, textarea, select')) return;

  // Check if the browser currently considers this input "user-invalid"
  const isUserInvalid = input.matches(':user-invalid');
  
  if (isUserInvalid) {
    input.setAttribute('aria-invalid', 'true');
  } else {
    input.removeAttribute('aria-invalid');
  }
};

// Listen on the document to handle dynamically added fields.
// 'blur' and 'focus' do not bubble, so we must use the capture phase (true).
document.addEventListener('blur', updateAriaState, true);
document.addEventListener('focus', updateAriaState, true);

// Also update on input if we've already shown the error, 
// so the error clears immediately when fixed.
document.addEventListener('input', (event) => {
  const input = event.target;
  if (!input.matches?.('input, textarea, select')) return;

  const hasAriaInvalid = input.hasAttribute('aria-invalid');
  const ariaInvalid = input.getAttribute('aria-invalid');
  if (hasAriaInvalid && ariaInvalid === 'true') {
    updateAriaState(event);
  }
});

फ़ॉलबैक और ब्राउज़र से जुड़ी सहायता

सुविधा की पहचान करना

सीएसएस और JavaScript में सहायता देखी जा सकती है.

JavaScript की जांच:

if (!CSS.supports('selector(:user-invalid)')) {
  // Fallback logic here
}

फ़ॉलबैक के लिए सीएसएस

यह पक्का करने के लिए कि आपका फ़ॉलबैक लॉजिक, नेटिव बिहेवियर से अलग न दिखे, आपको गड़बड़ी की स्टाइल को स्यूडो-क्लास और फ़ॉलबैक क्लास, दोनों पर लागू करना होगा.

/* Apply error styles to both native selector and fallback class */
input:user-invalid,
input.user-invalid-fallback {
  border-color: #d93025;
  background-color: #fce8e6;
}

/* Show error message for both cases */
input:user-invalid ~ .error-msg,
input.user-invalid-fallback ~ .error-msg {
  display: block;
}

फ़ॉलबैक लॉजिक

अगर :user-invalid मौजूद नहीं है, तो WeakMap का इस्तेमाल करके, इंटरैक्शन की स्थिति को मैन्युअल तरीके से ट्रैक करें.

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' && input.matches?.('form')) {
      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.matches?.('input, textarea, select')) 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 = () => {
    if (CSS.supports('selector(:user-invalid)')) return;

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

  return { init };
})();

// Initialize globally
UserInvalidFallback.init();

ध्यान में रखने लायक अन्य बातें

  1. aria-live बनाम aria-errormessage:

    • aria-errormessage, इनपुट को टेक्स्ट से कनेक्ट करता है. हालांकि, हो सकता है कि स्क्रीन रीडर, टेक्स्ट के दिखने पर तुरंत उसे न पढ़ें. वे ऐसा सिर्फ़ तब करते हैं, जब इनपुट पर फ़ोकस किया जाता है.
    • अगर आपको गड़बड़ी दिखने पर तुरंत सूचना चाहिए (उदाहरण के लिए, ब्लर होने पर), तो गड़बड़ी के मैसेज वाले कंटेनर में role="alert" या aria-live="polite" जोड़ें. हालांकि, इसकी अच्छी तरह से जांच करें, ताकि उपयोगकर्ता जब गड़बड़ी को ठीक करने के लिए फ़ील्ड पर फ़ोकस करे, तो "दो बार सूचना" न मिले.
  2. इंटरनैशनलाइज़ेशन:

    • पक्का करें कि गड़बड़ी के मैसेज (#email-error) के टेक्स्ट कॉन्टेंट का अनुवाद किया गया हो. लॉजिक पहले जैसा ही रहता है.