نظرة عامة أساسية حول كيفية إنشاء مكوِّن متعدد الاختيارات متجاوب وتكيّفي ويمكن الوصول إليه من أجل ترتيب تجارب المستخدمين وفلترتها
في هذه المشاركة، أرغب في مشاركة التفكير حول طريقة لإنشاء مكون متعدد التحديد. ننصحك بالاطّلاع على العرض التوضيحي.
في ما يلي إصدار YouTube من هذه المشاركة إذا كنت تفضّل الفيديوهات:
نظرة عامة
غالبًا ما يتم تقديم عناصر إلى المستخدمين، وأحيانًا الكثير من العناصر، وفي هذه الحالات، قد يكون من الجيد توفير طريقة لتقليل استخدام القائمة لمنع الاختيار الزائد. تستكشف مشاركة المدونة هذه تصفية واجهة المستخدم كطريقة لتقليل الخيارات. ويتم ذلك من خلال تقديم سمات العناصر التي يمكن للمستخدمين اختيارها أو إلغاء اختيارها، ما يقلّل من النتائج، وبالتالي تقلِّل أعباء الخيارات الزائدة.
التفاعلات
الهدف هو تمكين الاجتياز السريع لخيارات التصفية لجميع المستخدمين وأنواع المدخلات المختلفة. سيتم تسليم ذلك مع زوج قابل
للتكيف وسريع الاستجابة من المكونات. شريط جانبي تقليدي لمربّعات الاختيار لأجهزة الكمبيوتر المكتبي ولوحة المفاتيح وقارئات الشاشة، و<select
multiple>
لمستخدمي اللمس
إنّ هذا القرار المتعلق باستخدام ميزة "الاختيار المتعدد" المدمج للمس، وليس للكمبيوتر المكتبي، يساهم في توفير العمل وعلى إنشاء أعمال، ولكن أعتقد أنّ ذلك يوفّر تجارب مناسبة باستخدام رموز برمجية أقل مقارنةً ببناء تجربة سريعة الاستجابة في مكوّن واحد.
اللمس
ويوفر مكون اللمس المساحة ويساعد في دقة تفاعل المستخدم على الهاتف المحمول. لتوفير المساحة من خلال تصغير شريط جانبي كامل من مربّعات الاختيار في
تجربة لمس مضمَّنة في <select>
. يساعد في دقة الإدخال من خلال إظهار تجربة
تراكب لمس كبيرة يوفرها النظام.
لوحة مفاتيح ولوحة ألعاب
في ما يلي عرض توضيحي يوضِّح كيفية استخدام <select multiple>
من لوحة المفاتيح.
لا يمكن تصميم هذا التحديد المتعدد المدمج ولكن يتم تقديمه في تخطيط صغير غير مناسب لتقديم الكثير من الخيارات. هل ترى كيف لا يمكنك حقًا رؤية نطاق الخيارات الواسع في هذا المربع الصغير؟ بينما يمكنك تغيير حجمه، إلا أنه لا يزال غير قابل للاستخدام كشريط جانبي لمربعات الاختيار.
Markup
وسيتم تضمين كلا المكوّنَين في عنصر <form>
نفسه. ستتم ملاحظة نتائج هذا النموذج، سواء كانت مربعات اختيار أو تحديد متعدد، واستخدام هذه النتائج لفلترة الشبكة، ولكن يمكن أيضًا إرسالها إلى أحد الخوادم.
<form>
</form>
مكوِّن مربّعات الاختيار
يجب أن يتم لف مجموعات مربعات الاختيار في عنصر <fieldset>
ومنحها <legend>
.
عند تنظيم HTML بهذه الطريقة، ستفهم برامج قراءة الشاشة وFormData تلقائيًا علاقة العناصر.
<form>
<fieldset>
<legend>New</legend>
… checkboxes …
</fieldset>
</form>
عند استخدام التجميع، أضِف <label>
و<input type="checkbox">
لكل فلتر. لقد اخترتُ التفاف النص في <div>
لكي تتمكّن السمة gap
في CSS من
تباعدهما بشكل متساوٍ والحفاظ على المحاذاة عندما تصبح التصنيفات متعددة الأسطر.
<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
يوضح هذه الميزة. تبدأ عملية الدمج باستخدام HTML والسمة
role="status"
.
<div role="status" class="sr-only" id="applied-filters"></div>
سيقرأ هذا العنصر التغييرات التي يتم إجراؤها على المحتوى بصوت عالٍ. يمكننا تعديل المحتوى باستخدام دوال CSS أثناء تفاعل المستخدمين مع مربعات الاختيار. للقيام بذلك، نحتاج أولاً إلى إنشاء عدّاد باسم على العنصر الرئيسي للمدخلات وعنصر الحالة.
aside {
counter-reset: filters;
}
سيكون العدد تلقائيًا 0
، وهذا عدد رائع، وليس هناك أي سمة :checked
تلقائيًا في هذا التصميم.
بعد ذلك، سنستهدف عناصر <aside>
الثانوية :checked
بهدف زيادة العدّاد الذي أنشأناه حديثًا. وعندما يغير المستخدم حالة الإدخالات، سيظهر العدّاد filters
.
aside :checked {
counter-increment: filters;
}
أصبح CSS على دراية بالحصيلة العامة لواجهة مستخدم مربّعات الاختيار وعنصر دور الحالة فارغ وينتظر القيم. ولأنّ CSS تحتفظ بالسجلّ في الذاكرة، تتيح الدالة counter()
الوصول إلى القيمة من محتوى العنصر الزائف:
aside #applied-filters::before {
content: counter(filters) " filters ";
}
سيعلن HTML لعنصر دور الحالة الآن عن "فلترين " لقارئ الشاشة. هذه بداية جيدة، ولكن يمكننا القيام بعمل أفضل، مثل مشاركة عدد النتائج التي حدّثتها عوامل التصفية. سنقوم بهذا العمل من JavaScript، لأنه ليس بإمكان العدادات.
دمج الإثارة
كانت خوارزمية العدّادات رائعة مع CSS 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
لأنّه من الشائع
توصيل ماوس أو أي جهاز إدخال آخر عالي الدقة.
مجموعات الحقول
يكون النمط والتنسيق التلقائيان لـ <fieldset>
مع <legend>
فريدَين:
في العادة، أستخدم السمة 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=7&hl=ar)
الشبكة المتحركة
يتم تنفيذ الرسوم المتحركة للتنسيق من خلال 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>
أيضًا.
ينعكس اختيار عنصر <select>
في counter()
في قسم تسوية البيانات، تم بالفعل إنشاء أداة معالجة عند الإدخال. في نهاية هذه الدالة، يكون عدد الفلاتر المختارة وعدد النتائج لهذه الفلاتر معروفًا. يمكن تمرير القيم إلى عنصر دور الدولة مثل هذا.
let statusRoleElement = document.querySelector('#applied-filters')
statusRoleElement.style.counterSet = selectData.length
تنعكس النتائج في العنصر role="status"
توفّر ":checked
" طريقة مضمّنة لتمرير عدد الفلاتر المُختارة إلى
عنصر دور الحالة، ولكنّها تفتقر إلى إمكانية الاطّلاع على عدد النتائج المفلتَرة.
يمكن بلغة JavaScript رصد التفاعل مع مربّعات الاختيار، وبعد فلترة الشبكة، أضِف textContent
كما فعلت العنصر <select>
.
document
.querySelector('aside form')
.addEventListener('input', e => {
// isotope demo code
let filterResults = IsotopeGrid.getFilteredItemElements().length
document.querySelector('#applied-filters').textContent = `giving ${filterResults} results`
})
في إجمال هذا العمل يكمل الإعلان "فلتران يقدمان 25 نتيجة".
الآن سيتم تسليم خبرتنا الممتازة من التكنولوجيا المساعدة إلى جميع المستخدمين، مهما تفاعلوا معها.
الخلاصة
الآن بعد أن عرفت كيف فعلت ذلك، كيف يمكنك‽ 🙂
دعونا ننويع أساليبنا ونتعلم جميع طرق الإنشاء على الويب. يمكنك إنشاء عرض توضيحي وروابط تغريدات لي وسنضيفها إلى قسم الريمكسات في المنتدى أدناه.
ريمكسات من المنتدى
لا يتوفّر أي محتوى بعد.