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

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

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

डेमो

अगर आपको वीडियो देखना पसंद है, तो यहां इस पोस्ट का 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 स्क्रीन रीडर में चालू फ़िल्टर की संख्या के बारे में जानकारी देने वाला स्क्रीनशॉट.

नेस्ट किए जाने का रोमांच

CSS नेस्टिंग-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 के स्क्रीन रीडर से नतीजे सुनने का स्क्रीनशॉट.

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

नतीजा

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

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

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

फ़िलहाल, यहां कोई कॉन्टेंट मौजूद नहीं है!