उपयोगकर्ताओं के अनुभवों को क्रम से लगाने और फ़िल्टर करने के लिए, एक से ज़्यादा चुने गए कॉम्पोनेंट बनाने के तरीके के बारे में बुनियादी खास जानकारी. ये ऐसे कॉम्पोनेंट बनाने के तरीके के बारे में खास जानकारी देते हैं जो रिस्पॉन्सिव (स्क्रीन के हिसाब से आकार बदलने वाले), अडैप्टिव, और ऐक्सेस करने लायक हों और जिन्हें कई तरह से चुना जा सके.
इस पोस्ट में, हम आपको एक से ज़्यादा आइटम चुनने का विकल्प देने के बारे में बताना चाहते हैं. डेमो आज़माएं.
अगर आप वीडियो पसंद करते हैं, तो यहां इस पोस्ट का YouTube वर्शन दिया गया है:
खास जानकारी
उपयोगकर्ताओं को अक्सर आइटम दिखाए जाते हैं, कभी-कभी बहुत सारे आइटम दिखते हैं. ऐसे मामलों में, विकल्पों की संख्या ज़रूरत से ज़्यादा होने से बचाने के लिए, सूची को कम करने का एक अच्छा तरीका हो सकता है. इस ब्लॉग पोस्ट में, फ़िल्टर करने के यूज़र इंटरफ़ेस (यूआई) के बारे में जानकारी दी गई है. ऐसा करने के लिए, ऐसे आइटम एट्रिब्यूट की जानकारी दी जाती है जिन्हें उपयोगकर्ता चुन सकते हैं या चुने हुए का निशान हटा सकते हैं. इससे नतीजे कम होते हैं और विकल्प का ओवरलोड कम हो जाता है.
इंटरैक्शन
इसका मकसद है, सभी उपयोगकर्ताओं और उनके अलग-अलग तरह के इनपुट टाइप के लिए, फ़िल्टर के विकल्पों को तेज़ी से ट्रैवर्सल करना. यह कॉम्पोनेंट, ज़रूरत के हिसाब से बनाए जा सकने वाले और रिस्पॉन्सिव पेयर के साथ डिलीवर किया जाएगा. डेस्कटॉप, कीबोर्ड, और स्क्रीन रीडर के लिए चेकबॉक्स का एक पारंपरिक साइडबार. साथ ही, टच उपयोगकर्ताओं के लिए <select
multiple>
.
डेस्कटॉप के बजाय, टच के लिए बिल्ट-इन मल्टीसिलेक्ट का इस्तेमाल करने से काम बचता है और काम बेहतर होता है. हालांकि, मेरा मानना है कि किसी एक कॉम्पोनेंट में पूरे रिस्पॉन्सिव अनुभव को बनाने की तुलना में, कम कोड लोन के साथ सही अनुभव मिलता है.
टच
टच कॉम्पोनेंट, जगह की बचत करता है और मोबाइल पर उपयोगकर्ता के इंटरैक्शन को ज़्यादा सटीक बनाने में मदद करता है. यह चेकबॉक्स के पूरे साइडबार को <select>
बिल्ट-इन ओवरले टच अनुभव में छोटा करके, जगह बचाता है. यह सिस्टम से मिलने वाला एक बड़ा टच ओवरले अनुभव
दिखाकर इनपुट को सटीक बनाने में मदद करता है.
कीबोर्ड और गेमपैड
नीचे कीबोर्ड से <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
नहीं होता.
इसके बाद, अपने बनाए गए नए काउंटर को बढ़ाने के लिए, हम :checked
वाले <aside>
एलिमेंट के बच्चों को टारगेट करेंगे. जब उपयोगकर्ता इनपुट की स्थिति बदलता है,
तो filters
काउंटर की वैल्यू के हिसाब से गिनती होती है.
aside :checked {
counter-increment: filters;
}
सीएसएस को अब चेकबॉक्स के यूज़र इंटरफ़ेस (यूआई) के सामान्य टैली के बारे में पता है और स्टेटस रोल एलिमेंट खाली है और वैल्यू का इंतज़ार कर रहा है. सीएसएस, मेमोरी में टैली को बनाए रख रही है. इसलिए, counter()
फ़ंक्शन, गलत एलिमेंट कॉन्टेंट से वैल्यू को ऐक्सेस करने की अनुमति देता है:
aside #applied-filters::before {
content: counter(filters) " filters ";
}
स्थिति भूमिका तत्व का HTML अब स्क्रीन रीडर को "2 फ़िल्टर " का एलान करेगा. यह एक अच्छी शुरुआत है, लेकिन हम और बेहतर काम कर सकते हैं, जैसे कि फ़िल्टर अपडेट किए गए नतीजों का आंकड़ा शेयर करना. यह काम हम JavaScript से करेंगे, क्योंकि यह काउंटर के काम करने के तरीके से बाहर है.
Nesting के लिए रोमांच
सीएसएस नेस्टिंग-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
की तय सीमा से ज़्यादा लंबा होने पर, लेबल टेक्स्ट रैप हो सकता है. टेक्स्ट को रैप करना अच्छा है, लेकिन
टेक्स्ट और चेकबॉक्स के बीच अलाइनमेंट नहीं है. इसके लिए, Flexbox सबसे अच्छा विकल्प है.
fieldset > div {
display: flex;
gap: 2ch;
align-items: baseline;
}
![कई लाइनों में रैपिंग के मामले में, स्क्रीनशॉट में दिखाया गया है कि
सही का निशान, टेक्स्ट की पहली लाइन पर कैसे अलाइन होता है.](https://web.dev/static/articles/building/a-multi-select-component/image/screenshot-showing-the-c-b135285ef7d8a.png?authuser=6&hl=hi)
ऐनिमेशन वाला ग्रिड
लेआउट ऐनिमेशन Isotope ने किया है. इंटरैक्टिव सॉर्ट और फ़िल्टर के लिए एक बढ़िया और बेहतरीन प्लगिन.
JavaScript
व्यवस्थित ऐनिमेट किए गए, इंटरैक्टिव ग्रिड को व्यवस्थित करने में मदद करने के साथ-साथ, JavaScript का इस्तेमाल कुछ खुरदुरे किनारों को बेहतर करने के लिए किया जाता है.
लोगों के इनपुट को सामान्य बनाना
इस डिज़ाइन में इनपुट देने के दो अलग-अलग तरीकों का एक ही फ़ॉर्म है. साथ ही, ये एक ही क्रम से नहीं होते. हालांकि, कुछ JavaScript की मदद से हम डेटा को नॉर्मलाइज़ कर सकते हैं.
मैंने <select>
एलिमेंट के डेटा स्ट्रक्चर को, ग्रुप किए गए चेकबॉक्स के स्ट्रक्चर के साथ अलाइन
करने का विकल्प चुना है. ऐसा करने के लिए, input
इवेंट लिसनर को <select>
एलिमेंट में जोड़ा जाता है. इसके बाद, इसे
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>
एलिमेंट के विकल्पों की गिनती भी की जाए.
counter()
में दिखाया गया <select>
एलिमेंट का विकल्प
डेटा नॉर्मलाइज़ेशन सेक्शन में, इनपुट पर पहले ही एक लिसनर बनाया जा चुका है. इस फ़ंक्शन के आखिर में, चुने गए फ़िल्टर की संख्या और उन फ़िल्टर के लिए नतीजों की संख्या दिखती है. वैल्यू, इस तरह के स्टेट रोल एलिमेंट में पास की जा सकती हैं.
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 नतीजे देने वाले 2 फ़िल्टर" सूचना को पूरा करता है.
अब सभी उपयोगकर्ताओं को सहायक टेक्नोलॉजी का हमारा बेहतरीन अनुभव मिलेगा, भले ही वे इसके साथ इंटरैक्ट करते हों.
नतीजा
अब आपको पता चल गया है कि मैंने इसे कैसे किया, तो आप कैसी होंगी‽ 🙂
चलिए, इसे अलग-अलग तरीके से समझें और वेब पर सभी के काम करने के तरीके सीखें. एक डेमो तैयार करें, मुझे ट्वीट करें वाले लिंक, और मैं उसे नीचे दिए गए कम्यूनिटी रीमिक्स सेक्शन में जोड़ दूंगी!
कम्यूनिटी रीमिक्स
अभी यहां देखने के लिए कुछ नहीं है!