मेन्यू इंटरैक्शन चुनें

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

समस्या

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

समस्या का हल

:user-invalid स्यूडो-क्लास, <select> एलिमेंट के साथ आसानी से काम करती है. यह उपयोगकर्ता के इंटरैक्शन फ़्लो का पालन करता है: सिर्फ़ पेज लोड करने या बिना कोई बदलाव किए फ़ोकस/ब्लर करने को इंटरैक्शन नहीं माना जाता. इसलिए, फ़ील्ड तब तक न्यूट्रल रहता है, जब तक कि उपयोगकर्ता कोई विकल्प चुनने की कोशिश नहीं करता.

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

  1. एचटीएमएल की पाबंदी: required के साथ <select> का इस्तेमाल करें. पहले विकल्प में value="" होना चाहिए. साथ ही, सही विकल्प चुनने के लिए, इसे बंद/छिपाया जाना चाहिए.
  2. विज़ुअल फ़ीडबैक: चुने गए बॉक्स के बॉर्डर को स्टाइल करने के लिए, :user-invalid का इस्तेमाल करें.
  3. समय: अगर उपयोगकर्ता कंट्रोल को ब्लर करने से पहले या फ़ॉर्म सबमिट करने पर वैल्यू बदलता है, तो ब्राउज़र फ़ील्ड को "इंटरैक्ट किया गया" मानता है. भले ही, वैल्यू को वापस डिफ़ॉल्ट अमान्य स्थिति में बदल दिया गया हो.

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

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

यहां "प्लेसहोल्डर" विकल्प सबसे अहम है.

<form>
  <div class="field">
    <label for="country">Country</label>
    <select
      id="country"
      name="country"
      required
      aria-errormessage="country-error"
    >
      <option value="" disabled selected>Select a country...</option>
      <option value="us">United States</option>
      <option value="ca">Canada</option>
      <option value="uk">United Kingdom</option>
    </select>
    <div id="country-error" class="error-msg">
      Please select a country.
    </div>
  </div>
</form>

2. सीएसएस

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

/*
  Only show error after the user visits the select menu.
*/
select:user-invalid {
  border-color: #d93025;
  background-color: #fce8e6;
}

select:user-invalid + .error-msg {
  display: block;
}

select:user-valid {
  border-color: #188038;
}

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

:user-valid और :user-invalid के लिए फ़ॉलबैक और ब्राउज़र सहायता

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

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 फ़ॉलबैक

फिर से इस्तेमाल की जा सकने वाली ऐसी यूटिलिटी का इस्तेमाल करें जो 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') {
      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);

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

  1. मोबाइल पर काम करने का तरीका: मोबाइल डिवाइसों पर, ओएस पिकर के हिसाब से "धुंधला" करने का तरीका अलग हो सकता है. हमारा सुझाव है कि आप असली डिवाइसों पर टेस्ट करें.
  2. सुलभता: नेटिव :user-invalid, ARIA एट्रिब्यूट के साथ अपने-आप सिंक नहीं होता. aria-invalid को विज़ुअल स्टेट के साथ सिंक करने के लिए, यह JavaScript जोड़ें:
// 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);
});