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

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

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

إصدار تجريبي

في ما يلي إصدار 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>

لقطة شاشة تعرض معلومات حول عناصر وسيلة الإيضاح ومجموعة الحقول، وتعرض اللون واسم العنصر.

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

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

يتم تنفيذ الرسوم المتحركة للتنسيق من خلال Isotope. هو مكوّن إضافي فعّال وفعّال للفرز والتصفية التفاعليين.

JavaScript

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

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

الآن سيتم تسليم خبرتنا الممتازة من التكنولوجيا المساعدة إلى جميع المستخدمين، مهما تفاعلوا معها.

الخلاصة

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

دعونا ننويع أساليبنا ونتعلم جميع طرق الإنشاء على الويب. يمكنك إنشاء عرض توضيحي وروابط تغريدات لي وسنضيفها إلى قسم الريمكسات في المنتدى أدناه.

ريمكسات من المنتدى

لا يتوفّر أي محتوى بعد.