एक से ज़्यादा कॉम्पोनेंट चुनने की सुविधा का इस्तेमाल करना

उपयोगकर्ता अनुभव को क्रम से लगाने और फ़िल्टर करने के लिए, रिस्पॉन्सिव, अडैप्टिव, और ऐक्सेस किए जा सकने वाले मल्टी-सिलेक्ट कॉम्पोनेंट बनाने का बुनियादी तरीका.

इस पोस्ट में, मैं एक से ज़्यादा आइटम चुनने की सुविधा देने वाले कॉम्पोनेंट को बनाने के तरीके के बारे में बताना चाहता हूं. डेमो आज़माएं.

डेमो

अगर आपको वीडियो देखना पसंद है, तो यहां इस पोस्ट का YouTube वर्शन दिया गया है:

खास जानकारी

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

इंटरैक्शन

इसका मकसद, सभी उपयोगकर्ताओं और उनके अलग-अलग इनपुट टाइप के लिए, फ़िल्टर के विकल्पों को तेज़ी से ट्रैवर्स करने की सुविधा देना है. इसे अडैप्ट करने लायक और रिस्पॉन्सिव कॉम्पोनेंट के साथ डिलीवर किया जाएगा. डेस्कटॉप, कीबोर्ड, और स्क्रीन रीडर के लिए चेकबॉक्स वाला पारंपरिक साइडबार. साथ ही, टच का इस्तेमाल करने वाले लोगों के लिए <select multiple>.

डेस्कटॉप के लाइट और डार्क मोड की तुलना करने वाला स्क्रीनशॉट. इसमें चेकबॉक्स के साइडबार के साथ-साथ, मोबाइल के iOS और Android वर्शन में मौजूद मल्टी-सिलेक्ट एलिमेंट की तुलना की गई है.

टच के लिए पहले से मौजूद मल्टी-सिलेक्ट का इस्तेमाल करने और डेस्कटॉप के लिए नहीं करने का यह फ़ैसला, काम को आसान बनाता है और काम बढ़ाता है. हालांकि, मेरा मानना है कि एक कॉम्पोनेंट में पूरा रिस्पॉन्सिव अनुभव बनाने के बजाय, कम कोड डेट के साथ सही अनुभव मिलता है.

छूकर

टच कॉम्पोनेंट की मदद से, जगह बचती है और मोबाइल पर उपयोगकर्ता इंटरैक्शन को सटीक बनाने में मदद मिलती है. यह चेकबॉक्स के पूरे साइडबार को <select> में पहले से मौजूद ओवरले टच अनुभव में छोटा करके जगह बचाता है. यह सिस्टम की मदद से, बड़ा टच ओवरले दिखाकर, सटीक जानकारी डालने में मदद करता है.

Android, iPhone, और iPad पर Chrome में, एक से ज़्यादा आइटम चुनने की सुविधा वाले एलिमेंट की स्क्रीनशॉट झलक. iPad और iPhone पर, एक से ज़्यादा आइटम चुनने की सुविधा टॉगल करके चालू की जा सकती है. साथ ही, हर डिवाइस पर स्क्रीन साइज़ के हिसाब से, यूनीक अनुभव मिलता है.

कीबोर्ड और गेमपैड

यहां कीबोर्ड से <select multiple> का इस्तेमाल करने का तरीका बताया गया है.

पहले से मौजूद इस मल्टी-सिलेक्ट को स्टाइल नहीं किया जा सकता. साथ ही, यह सिर्फ़ एक छोटे लेआउट में उपलब्ध है, जो कई विकल्पों को दिखाने के लिए सही नहीं है. देखें कि छोटे बॉक्स में, विकल्पों की पूरी जानकारी कैसे नहीं दिखती? हालांकि, इसका साइज़ बदला जा सकता है, लेकिन फिर भी इसे चेकबॉक्स के साइडबार के तौर पर इस्तेमाल नहीं किया जा सकता.

मार्कअप

दोनों कॉम्पोनेंट एक ही <form> एलिमेंट में शामिल होंगे. इस फ़ॉर्म के नतीजों को देखा जाएगा और ग्रिड को फ़िल्टर करने के लिए उनका इस्तेमाल किया जाएगा. भले ही, ये नतीजे चेकबॉक्स या एक से ज़्यादा विकल्प चुनने वाले फ़ील्ड से मिले हों. साथ ही, इन्हें सर्वर पर सबमिट भी किया जा सकता है.

<form>

</form>

चेकबॉक्स कॉम्पोनेंट

चेकबॉक्स के ग्रुप को <fieldset> एलिमेंट में रैप किया जाना चाहिए और उन्हें <legend> दिया जाना चाहिए. जब एचटीएमएल को इस तरह से व्यवस्थित किया जाता है, तो स्क्रीन रीडर और FormData, एलिमेंट के बीच के संबंध को अपने-आप समझ जाएंगे.

<form>
  <fieldset>
    <legend>New</legend>
    … checkboxes …
  </fieldset>
</form>

ग्रुपिंग करने के बाद, हर फ़िल्टर के लिए <label> और <input type="checkbox"> जोड़ें. मैंने अपने लेबल को <div> में रैप किया है, ताकि सीएसएस gap प्रॉपर्टी, लेबल के एक से ज़्यादा लाइन में होने पर, उन्हें बराबर स्पेस दे सके और अलाइनमेंट बनाए रख सके.

<form>
  <fieldset>
    <legend>New</legend>
    <div>
      <input type="checkbox" id="last 30 days" name="new" value="last 30 days">
      <label for="last 30 days">Last 30 Days</label>
    </div>
    <div>
      <input type="checkbox" id="last 6 months" name="new" value="last 6 months">
      <label for="last 6 months">Last 6 Months</label>
    </div>
   </fieldset>
</form>

लेजेंड और
  फ़ील्डसेट एलिमेंट के लिए जानकारी देने वाले ओवरले वाला स्क्रीनशॉट, रंग और एलिमेंट का नाम दिखाता है.

<select multiple> कॉम्पोनेंट

<select> एलिमेंट की एक ऐसी सुविधा है जिसका इस्तेमाल बहुत कम किया जाता है. वह सुविधा है multiple. जब इस एट्रिब्यूट का इस्तेमाल <select> एलिमेंट के साथ किया जाता है, तो उपयोगकर्ता को सूची से कई विकल्प चुनने की अनुमति होती है. यह इंटरैक्शन को रेडियो सूची से चेकबॉक्स सूची में बदलने जैसा है.

<form>
  <select multiple="true" title="Filter results by category">
    …
  </select>
</form>

<select> में ग्रुप बनाने और उन्हें लेबल करने के लिए, <optgroup> एलिमेंट का इस्तेमाल करें. साथ ही, उसे label एट्रिब्यूट और वैल्यू दें. यह एलिमेंट और एट्रिब्यूट वैल्यू, <fieldset> और <legend> एलिमेंट की तरह ही होती है.

<form>
  <select multiple="true" title="Filter results by category">
    <optgroup label="New">
      …
    </optgroup>
  </select>
</form>

अब फ़िल्टर के लिए, <option> एलिमेंट जोड़ें.

<form>
  <select multiple="true" title="Filter results by category">
    <optgroup label="New">
      <option value="last 30 days">Last 30 Days</option>
      <option value="last 6 months">Last 6 Months</option>
    </optgroup>
  </select>
</form>

एक से ज़्यादा आइटम चुनने की सुविधा वाले एलिमेंट के डेस्कटॉप रेंडरिंग का स्क्रीनशॉट.

सहायक टेक्नोलॉजी को सूचना देने के लिए, काउंटर की मदद से इनपुट ट्रैक करना

इस यूज़र एक्सपीरियंस में, स्टेटस की भूमिका वाली तकनीक का इस्तेमाल किया जाता है. इससे स्क्रीन रीडर और अन्य सहायक टेक्नोलॉजी के लिए, फ़िल्टर की संख्या को ट्रैक और मैनेज किया जा सकता है. YouTube वीडियो में इस सुविधा के बारे में बताया गया है. इंटिग्रेशन, एचटीएमएल और एट्रिब्यूट role="status" से शुरू होता है.

<div role="status" class="sr-only" id="applied-filters"></div>

यह एलिमेंट, कॉन्टेंट में किए गए बदलावों को ज़ोर से पढ़कर सुनाएगा. जब उपयोगकर्ता चेकबॉक्स के साथ इंटरैक्ट करते हैं, तो हम सीएसएस काउंटर की मदद से कॉन्टेंट को अपडेट कर सकते हैं. ऐसा करने के लिए, हमें सबसे पहले इनपुट और स्टेटस एलिमेंट के पैरंट एलिमेंट पर, नाम वाला एक काउंटर बनाना होगा.

aside {
  counter-reset: filters;
}

डिफ़ॉल्ट रूप से, गिनती 0 होगी, जो बहुत बढ़िया है. इस डिज़ाइन में, डिफ़ॉल्ट रूप से कुछ भी :checked नहीं होता.

इसके बाद, अपने नए बनाए गए काउंटर को बढ़ाने के लिए, हम <aside> एलिमेंट के उन चाइल्ड एलिमेंट को टारगेट करेंगे जो :checked हैं. जब उपयोगकर्ता इनपुट की स्थिति बदलता है, तो filters काउंटर बढ़ता जाएगा.

aside :checked {
  counter-increment: filters;
}

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

aside #applied-filters::before {
  content: counter(filters) " filters ";
}

स्टेटस की भूमिका वाले एलिमेंट के एचटीएमएल से, अब स्क्रीन रीडर को "दो फ़िल्टर " का एलान किया जाएगा. यह एक अच्छी शुरुआत है, लेकिन हम और भी बेहतर कर सकते हैं. जैसे, फ़िल्टर की मदद से अपडेट किए गए नतीजों की संख्या शेयर करना. हम यह काम JavaScript से करेंगे, क्योंकि यह काउंटर के काम से बाहर है.

MacOS स्क्रीन रीडर का स्क्रीनशॉट, जिसमें सक्रिय फ़िल्टर की संख्या बताई जा रही है.

नेस्टिंग एक्साइटमेंट

सीएसएस नेस्टिंग-1 के साथ काउंटर एल्गोरिदम काफ़ी अच्छा लगा, क्योंकि मुझे सारा लॉजिक एक ब्लॉक में डालने में मदद मिली. इसे पढ़ने और अपडेट करने के लिए, पोर्टेबल और एक ही जगह से ऐक्सेस किया जा सकता है.

aside {
  counter-reset: filters;

  & :checked {
    counter-increment: filters;
  }

  & #applied-filters::before {
    content: counter(filters) " filters ";
  }
}

लेआउट

इस सेक्शन में, दोनों कॉम्पोनेंट के लेआउट के बारे में बताया गया है. ज़्यादातर लेआउट स्टाइल, डेस्कटॉप चेकबॉक्स कॉम्पोनेंट के लिए होते हैं.

फ़ॉर्म

उपयोगकर्ताओं के लिए, फ़ॉर्म को आसानी से पढ़ने और स्कैन करने के लिए, फ़ॉर्म की चौड़ाई को ज़्यादा से ज़्यादा 30 वर्णों तक रखा जाता है. साथ ही, हर फ़िल्टर लेबल के लिए ऑप्टिकल लाइन की चौड़ाई सेट की जाती है. फ़ॉर्म में ग्रिड लेआउट और gap प्रॉपर्टी का इस्तेमाल करके, फ़ील्ड के बीच स्पेस दिया गया है.

form {
  display: grid;
  gap: 2ch;
  max-inline-size: 30ch;
}

<select> एलिमेंट

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

@media (pointer: coarse) {
  select[multiple] {
    display: block;
  }
}

coarse की वैल्यू से पता चलता है कि उपयोगकर्ता अपने मुख्य इनपुट डिवाइस से, स्क्रीन पर सटीक तरीके से इंटरैक्ट नहीं कर पाएगा. मोबाइल डिवाइस पर, पॉइंटर वैल्यू अक्सर coarse होती है, क्योंकि मुख्य इंटरैक्शन टच होता है. डेस्कटॉप डिवाइस पर, पॉइंटर की वैल्यू अक्सर fine होती है, क्योंकि आम तौर पर उस पर माउस या कोई ऐसा इनपुट डिवाइस कनेक्ट होता है जिसकी सटीक जानकारी मिलती है.

फ़ील्डसेट

<legend> के साथ <fieldset> की डिफ़ॉल्ट स्टाइल और लेआउट यूनीक होता है:

फ़ील्डसेट और लेजेंड के लिए डिफ़ॉल्ट स्टाइल का स्क्रीनशॉट.

आम तौर पर, अपने चाइल्ड एलिमेंट के बीच स्पेस देने के लिए, मैं gap प्रॉपर्टी का इस्तेमाल करता/करती हूं. हालांकि, <legend> की यूनीक पोज़िशनिंग की वजह से, चाइल्ड एलिमेंट के बीच बराबर स्पेस वाला सेट बनाना मुश्किल हो जाता है. gap के बजाय, आस-पास के सिबलिंग सिलेक्टर और margin-block-start का इस्तेमाल किया जाता है.

fieldset {
  padding: 2ch;

  & > div + div {
    margin-block-start: 2ch;
  }
}

इससे <legend> को सिर्फ़ <div> बच्चों को टारगेट करके, अपने स्पेस में बदलाव करने से रोका जा सकता है.

इनपुट के बीच मार्जिन की स्पेसिंग दिखाने वाला स्क्रीनशॉट, लेकिन इसमें लेजेंड नहीं है.

फ़िल्टर का लेबल और चेकबॉक्स

<fieldset> के डायरेक्ट चाइल्ड के तौर पर और फ़ॉर्म के 30ch की ज़्यादा से ज़्यादा चौड़ाई में, लेबल टेक्स्ट बहुत लंबा होने पर रैप हो सकता है. टेक्स्ट को रैप करना अच्छा है, लेकिन टेक्स्ट और चेकबॉक्स के बीच अलाइनमेंट ठीक न होना अच्छा नहीं है. इसके लिए, फ़्लेक्सबॉक्स सबसे सही है.

fieldset > div {
  display: flex;
  gap: 2ch;
  align-items: baseline;
}
स्क्रीनशॉट, जिसमें दिखाया गया है कि कई लाइन में टेक्स्ट रैप होने पर, सही का निशान टेक्स्ट की पहली लाइन के साथ कैसे अलाइन होता है.
इस Codepen में ज़्यादा खेलें

ऐनिमेटेड ग्रिड

लेआउट ऐनिमेशन, Isotope की मदद से किया जाता है. इंटरैक्टिव क्रम से लगाने और फ़िल्टर करने के लिए, बेहतर परफ़ॉर्म करने वाला और बेहतर प्लग इन.

JavaScript

JavaScript का इस्तेमाल, ऐनिमेशन वाले बेहतरीन और इंटरैक्टिव ग्रिड को ऑर्गनाइज़ करने के साथ-साथ, कुछ अन्य कामों के लिए भी किया जाता है.

उपयोगकर्ता के इनपुट को सामान्य करना

इस डिज़ाइन में एक फ़ॉर्म है, जिसमें इनपुट देने के दो अलग-अलग तरीके हैं. साथ ही, ये एक जैसे सीरियलाइज़ नहीं होते. हालांकि, कुछ JavaScript की मदद से, हम डेटा को सामान्य कर सकते हैं.

DevTools JavaScript कंसोल का स्क्रीनशॉट, जिसमें
  लक्ष्य और सामान्य डेटा के नतीजे दिख रहे हैं.

मैंने <select> एलिमेंट के डेटा स्ट्रक्चर को, ग्रुप किए गए चेकबॉक्स के स्ट्रक्चर के साथ अलाइन करने का विकल्प चुना है. ऐसा करने के लिए, <select> एलिमेंट में एक input इवेंट लिसनर जोड़ा जाता है. इसके बाद, इसे selectedOptions मैप किया जाता है.

document.querySelector('select').addEventListener('input', event => {
  // make selectedOptions iterable then reduce a new array object
  let selectData = Array.from(event.target.selectedOptions).reduce((data, opt) => {
    // parent optgroup label and option value are added to the reduce aggregator
    data.push([opt.parentElement.label.toLowerCase(), opt.value])
    return data
  }, [])
})

अब फ़ॉर्म सबमिट किया जा सकता है. इसके अलावा, इस डेमो के मामले में, Isotope को यह निर्देश दें कि किस आधार पर फ़िल्टर करना है.

स्टेटस की भूमिका वाला एलिमेंट बनाना

यह एलिमेंट, चेकबॉक्स के इंटरैक्शन के आधार पर सिर्फ़ फ़िल्टर की संख्या की गिनती कर रहा है और उसे दिखा रहा है. हालांकि, मुझे लगता है कि नतीजों की संख्या भी शेयर करना एक अच्छा आइडिया है. साथ ही, यह पक्का करना भी ज़रूरी है कि <select> एलिमेंट के विकल्पों की गिनती भी की जाए.

<select> एलिमेंट की पसंद, counter() में दिखती है

डेटा नॉर्मलाइज़ेशन सेक्शन में, इनपुट के लिए पहले से ही एक लिसनर बनाया गया था. इस फ़ंक्शन के आखिर में, चुने गए फ़िल्टर की संख्या और उन फ़िल्टर के लिए नतीजों की संख्या पता होती है. वैल्यू को राज्य की भूमिका वाले एलिमेंट में इस तरह पास किया जा सकता है.

let statusRoleElement = document.querySelector('#applied-filters')
statusRoleElement.style.counterSet = selectData.length

role="status" एलिमेंट में दिखने वाले नतीजे

:checked में, चुने गए फ़िल्टर की संख्या को स्टेटस भूमिका एलिमेंट में भेजने का एक तरीका पहले से मौजूद होता है. हालांकि, इसमें फ़िल्टर किए गए नतीजों की संख्या नहीं दिखती. JavaScript, चेकबॉक्स के साथ इंटरैक्शन को देख सकता है और ग्रिड को फ़िल्टर करने के बाद, <select> एलिमेंट की तरह textContent जोड़ सकता है.

document
  .querySelector('aside form')
  .addEventListener('input', e => {
    // isotope demo code
    let filterResults = IsotopeGrid.getFilteredItemElements().length
    document.querySelector('#applied-filters').textContent = `giving ${filterResults} results`
})

इस काम से, "दो फ़िल्टर से 25 नतीजे मिलते हैं" एलान पूरा हो जाता है.

MacOS के स्क्रीन रीडर से नतीजों के बारे में सुनाई गई सूचना का स्क्रीनशॉट.

अब सभी उपयोगकर्ताओं को, सहायता वाली टेक्नोलॉजी का बेहतरीन अनुभव मिलेगा. भले ही, वे इस टेक्नोलॉजी के साथ किसी भी तरह से इंटरैक्ट करें.

नतीजा

अब आपको पता है कि मैंने यह कैसे किया, तो आप कैसे करेंगे‽ 🙂

आइए, अलग-अलग तरीकों का इस्तेमाल करके, वेब पर कॉन्टेंट बनाने के सभी तरीके जानें. डेमो बनाएं और मुझे ट्वीट करें लिंक भेजें. हम इसे कम्यूनिटी रीमिक्स सेक्शन में जोड़ देंगे!

कम्यूनिटी रीमिक्स

अभी यहां देखने के लिए कुछ नहीं है!