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

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

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

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

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

نظرة عامة

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

التفاعلات

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

لقطة شاشة مقارنة تعرض الوضعين الفاتح والداكن على الكمبيوتر المكتبي مع شريط جانبي يتضمّن مربّعات اختيار، مقارنةً بجهاز iOS الجوّال وجهاز Android الجوّال مع عنصر اختيار متعدّد.

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

لمس

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

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

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

في ما يلي توضيح لكيفية استخدام <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>

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

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

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

الخاتمة

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

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

ريمكسات من إنشاء المنتدى

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