إنشاء مكوِّن متعدد الاختيارات

نظرة عامة أساسية حول كيفية إنشاء مكوّن اختيار متعدّد متجاوب وقابل للتكيّف وسهل الاستخدام لتصنيف تجارب المستخدمين وفلترتها

في هذه المشاركة، أريد مشاركة أفكار حول طريقة إنشاء مكوّن اختيار متعدّد. جرِّب الإصدار التجريبي.

العرض التوضيحي

إذا كنت تفضّل مشاهدة الفيديوهات، يمكنك الاطّلاع على نسخة من هذه المشاركة على YouTube:

نظرة عامة

غالبًا ما يتم عرض عناصر على المستخدمين، وفي بعض الأحيان العديد من العناصر، وفي هذه الحالات، قد يكون من المفيد توفير طريقة لتقليل القائمة لتجنّب الشعور بالارتباك بسبب كثرة الخيارات. تتناول مشاركة المدونة هذه واجهة مستخدم الفلترة كطريقة لتقليل الخيارات. ويتم ذلك من خلال عرض سمات السلع التي يمكن للمستخدمين اختيارها أو إلغاء اختيارها، ما يؤدي إلى تقليل النتائج وبالتالي تقليل عدد الخيارات المعروضة.

التفاعلات

والهدف من ذلك هو السماح بالتنقّل السريع بين خيارات الفلترة لجميع المستخدمين و أنواع الإدخال المختلفة. سيتم تقديم ذلك مع زوج من المكوّنات القابلة للتكيّف والمتوافقة مع مختلف الأجهزة. شريط جانبي تقليدي يتضمّن مربّعات اختيار لأجهزة الكمبيوتر المكتبي ولوحة المفاتيح وتطبيقات قراءة الشاشة، و<select multiple> لمستخدمي الأجهزة التي تعمل باللمس

لقطة شاشة للمقارنة تعرض وضعَي الإضاءة العادية والداكنة على أجهزة الكمبيوتر المكتبي مع شريط جانبي من مربّعات الاختيار مقارنةً بأجهزة iOS وAndroid الجوّالة التي تتضمّن عنصر اختيار متعدّد

إنّ قرار استخدام ميزة "الاختيار المتعدّد" المضمّنة للأجهزة التي تعمل باللمس وليس لأجهزة الكمبيوتر المكتبي يوفر الجهد ويخلق العمل، ولكنّني أعتقد أنّه يقدّم تجارب مناسبة مع تقليل عبء الرموز البرمجية مقارنةً بإنشاء تجربة الاستجابة الكاملة في مكوّن واحد.

اللمس

يوفر مكوّن اللمس مساحة ويساعد في دقة تفاعل المستخدم على الأجهزة الجوّالة. ويساعد ذلك في توفير المساحة من خلال تصغير شريط جانبي كامل من مربّعات الاختيار إلى <select> تجربة لمس مدمجة. ويساعد ذلك في دقة الإدخال من خلال عرض تجربة كبيرة للشاشة التي تعمل باللمس يوفّرها النظام.


معاينة لقطة شاشة لعنصر الاختيار المتعدّد في Chrome على أجهزة Android وiPhone
وiPad تم تفعيل ميزة الاختيار المتعدّد على أجهزة iPad وiPhone، ويحصل كل جهاز على تجربته الخاصة المحسّنة لحجم الشاشة.

لوحة المفاتيح وجهاز التحكّم في الألعاب

في ما يلي عرض توضيحي لكيفية استخدام <select multiple> من لوحة المفاتيح.

لا يمكن تصميم ميزة الاختيار المتعدّد المضمّنة هذه، ويتم عرضها فقط في تنسيق ملف شخصي محدود غير مناسب لعرض الكثير من الخيارات. هل ترى أنّه لا يمكنك الاطّلاع على مجموعة الخيارات الواسعة في هذا المربّع الصغير؟ على الرغم من أنّه يمكنك تغيير حجمه، فإنه لا يزال أقل سهولة في الاستخدام من الشريط الجانبي الذي يتضمّن مربّعات اختيار.

Markup

سيتم تضمين كلا المكوّنين في عنصر <form> نفسه. سيتم رصد نتائج هذا النموذج، سواء كانت مربّعات اختيار أو قائمة اختيار متعدّدة، واستخدامها لfiltrare الشبكة، ولكن يمكن أيضًا إرسالها إلى خادم.

<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>

لقطة شاشة تتضمّن تراكبًا إعلاميًا لوسيلة الإيضاح وعناصر fieldset، تعرِض اللون واسم العنصر

مكوّن <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 عندما يتفاعل المستخدمون مع مربّعات الاختيار. لإجراء ذلك، نحتاج أولاً إلى إنشاء ملف شخصي يحتوي على اسم في عنصر رئيسي للعناصر inputs وstate.

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، لأنّه خارج نطاق ما يمكن أن تُنفّذه العدّادات.

لقطة شاشة لقارئ شاشة نظام التشغيل 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 لأنّه من الشائع أن يكون هناك ماوس أو جهاز إدخال آخر عالي الدقة متصلاً.

مجموعات الحقول

النمط والتصميم التلقائيان لرمز <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;
}
لقطة شاشة تعرِض كيفية محاذاة علامة الاختيار مع
    السطر الأول من النص في سيناريو لفّ النص على عدة أسطر
يمكنك تشغيل المزيد من المحتوى في هذا Codepen

الشبكة المتحركة

يتم إنشاء الصورة المتحركة للتنسيق بواسطة Isotope. مكوّن إضافي فعال وفعّال للفلترة والترتيب التفاعليَين

JavaScript

بالإضافة إلى المساعدة في تنظيم شبكة تفاعلية متحركة وأنيقة، يتم استخدام JavaScript لتحسين بعض الجوانب.

تطبيع إدخال المستخدم

يتضمّن هذا التصميم نموذجًا واحدًا يتيح طريقتَين مختلفتَين لتقديم الإدخال، وهما لا تتم تسلسل بنفس الطريقة. باستخدام بعض رموز JavaScript، يمكننا تسويتها.

لقطة شاشة لوحدة تحكّم JavaScript في DevTools والتي
  تعرِض الهدف ونتائج البيانات التي تمت تسويتها

اخترت مواءمة بنية بيانات العنصر <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 نتيجة".

لقطة شاشة لقارئ شاشة نظام التشغيل MacOS يعلن عن النتائج

ستتوفّر الآن تجربة التكنولوجيا المساعِدة الممتازة لجميع المستخدمين، بغض النظر عن طريقة تفاعلهم معها.

الخاتمة

الآن بعد أن عرفت كيف فعلت ذلك، كيف ستفعل ذلك؟ 🙂

لننوّع أساليبنا ونتعرّف على جميع الطرق لإنشاء تطبيقات على الويب. أنشئ عرضًا توضيحيًا وأرسِل إلينا رابطًا على Twitter، وسنضيفه إلى قسم الريمكسات التي أنشأها المستخدمون أدناه.

الريمكسات التي أنشأها المستخدمون

ما مِن عناصر للاطّلاع عليها هنا حتى الآن.