Membuat komponen multi-pilihan

Ringkasan dasar tentang cara membangun komponen multi-pilihan yang responsif, adaptif, dan mudah diakses untuk mengurutkan dan memfilter pengalaman pengguna.

Dalam postingan ini saya ingin berbagi pemikiran tentang cara membangun komponen multi-pilihan. Mulai demo.

Demo

Jika Anda lebih suka menonton video, berikut versi YouTube untuk postingan ini:

Ringkasan

Pengguna sering kali diperlihatkan item, terkadang banyak item, dan dalam mungkin menjadi ide yang baik untuk menyediakan cara mengurangi daftar untuk mencegah pilihan berlebihan. Ini posting blog mengeksplorasi UI pemfilteran sebagai cara untuk mengurangi pilihan. Hal ini dilakukan dengan menampilkan atribut item yang dapat dipilih atau dibatalkan pengguna, sehingga mengurangi hasil dan karenanya mengurangi kelebihan pilihan.

Interaksi

Tujuannya adalah untuk memungkinkan traversal opsi filter yang cepat untuk semua pengguna dan berbagai tipe input. Presentasi ini akan disampaikan dengan model sepasang komponen. Sidebar tradisional kotak centang untuk desktop dan keyboard dan pembaca layar, serta <select multiple> untuk pengguna sentuh.

Screenshot perbandingan yang menampilkan desktop terang dan gelap dengan sidebar
kotak centang vs seluler iOS dan Android dengan elemen multi-pilihan.

Keputusan untuk menggunakan multi-pilihan bawaan untuk sentuhan, dan bukan untuk desktop, ini menghemat pekerjaan dan menciptakan pekerjaan, tetapi saya yakin memberikan pengalaman yang sesuai dengan lebih sedikit hutang kode daripada membangun seluruh pengalaman responsif dalam satu komponen.

Sentuh

Komponen sentuh menghemat ruang dan membantu akurasi interaksi pengguna pada seluler. Cara ini menghemat ruang dengan menciutkan seluruh bilah sisi kotak centang menjadi <select> pengalaman sentuh overlay bawaan. Hal ini membantu akurasi input dengan menunjukkan pengalaman overlay sentuh besar yang disediakan oleh sistem.

J
pratinjau screenshot dari elemen multi-pilihan di Chrome pada Android, iPhone, dan
iPad. iPad dan iPhone mengaktifkan tombol multi-pilihan, dan masing-masing mendapatkan
pengalaman unik yang dioptimalkan
untuk ukuran layar.

Keyboard dan gamepad

Di bawah ini adalah demonstrasi cara menggunakan <select multiple> dari keyboard.

Multi-pilihan bawaan ini tidak dapat ditata dan hanya ditawarkan dalam model tidak cocok untuk menyajikan banyak pilihan. Lihat bagaimana Anda tidak bisa melihat luasnya pilihan di kotak kecil itu? Meskipun Anda dapat mengubah ukurannya, itu adalah masih belum dapat digunakan seperti bilah sisi kotak centang.

Markup

Kedua komponen akan dimuat dalam elemen <form> yang sama. Hasil formulir ini, baik kotak centang atau multi-pilihan, akan diamati dan digunakan untuk menyaring kisi-kisi, tetapi juga dapat dikirim ke server.

<form>

</form>

Komponen kotak centang

Kelompok kotak centang harus dikemas dalam satu <fieldset> dan diberi <legend>. Ketika HTML disusun seperti ini, pembaca layar dan FormData akan secara otomatis memahami hubungan elemen-elemen.

<form>
  <fieldset>
    <legend>New</legend>
    … checkboxes …
  </fieldset>
</form>

Setelah pengelompokan diterapkan, tambahkan <label> dan <input type="checkbox"> untuk masing-masing filter. Saya memilih untuk menggabungkannya di <div> sehingga properti gap CSS dapat mengatur jaraknya secara merata dan mempertahankan keselarasan saat label multigaris.

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

Screenshot dengan overlay informatif untuk legenda dan
  elemen {i>fieldset<i}, menunjukkan warna dan nama elemen.

<select multiple> komponen

Fitur yang jarang digunakan dari elemen <select> multiple. Jika atribut digunakan dengan elemen <select>, pengguna akan diizinkan untuk pilih banyak dari daftar. Ini seperti mengubah interaksi dari sebuah daftar radio ke daftar kotak centang.

<form>
  <select multiple="true" title="Filter results by category">
    …
  </select>
</form>

Untuk memberi label dan membuat grup di dalam <select>, gunakan <optgroup> dan berikan atribut serta nilai label. Elemen dan atribut ini nilainya mirip dengan elemen <fieldset> dan <legend>.

<form>
  <select multiple="true" title="Filter results by category">
    <optgroup label="New">
      …
    </optgroup>
  </select>
</form>

Sekarang tambahkan <option> elemen filter.

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

Screenshot rendering desktop dari elemen multi-pilihan.

Melacak input dengan penghitung untuk menginformasikan teknologi pendukung

Status peran digunakan dalam pengalaman pengguna ini, untuk melacak dan mempertahankan filter untuk pembaca layar dan teknologi bantuan lainnya. Video YouTube mendemonstrasikan fitur tersebut. Integrasi dimulai dengan HTML dan atribut role="status".

<div role="status" class="sr-only" id="applied-filters"></div>

Elemen ini akan membacakan dengan lantang perubahan yang dibuat pada konten. Kita dapat memperbarui konten dengan CSS penghitung saat pengguna berinteraksi dengan kotak centang. Untuk melakukan itu, pertama-tama kita perlu membuat dengan nama pada elemen induk dari input dan elemen status.

aside {
  counter-reset: filters;
}

Secara default, jumlahnya adalah 0, yang berarti tidak ada yang bernilai :checked {i>default<i} dalam desain ini.

Selanjutnya, untuk menambah penghitung yang baru dibuat, kita akan menargetkan turunan dari Elemen <aside> yang merupakan :checked. Saat pengguna mengubah status input, penghitung filters akan dihitung.

aside :checked {
  counter-increment: filters;
}

CSS kini mengetahui penghitungan umum UI kotak centang dan peran status kosong dan menunggu nilai. Karena CSS mempertahankan penghitungan memori, counter() memungkinkan pengaksesan nilai dari pseudo elemen konten:

aside #applied-filters::before {
  content: counter(filters) " filters ";
}

HTML untuk elemen peran status sekarang akan mengumumkan "2 filter " ke layar pembaca. Ini adalah awal yang baik, tetapi kita dapat melakukan yang lebih baik, seperti membagikan hasil hasil yang diperbarui oleh filter. Kita akan melakukan pekerjaan ini dari JavaScript, karena selain apa yang bisa dilakukan oleh penghitung.

Screenshot pembaca layar MacOS yang mengumumkan jumlah filter aktif.

Peningkatan antusiasme

Algoritma penghitung terasa hebat dengan CSS nesting-1, karena saya bisa menempatkan semua logika ke dalam satu blok. Terasa portabel dan terpusat untuk membaca dan memperbarui.

aside {
  counter-reset: filters;

  & :checked {
    counter-increment: filters;
  }

  & #applied-filters::before {
    content: counter(filters) " filters ";
  }
}

Tata letak

Bagian ini menjelaskan tata letak antara kedua komponen tersebut. Sebagian besar gaya tata letak adalah untuk komponen kotak centang desktop.

Formulir

Untuk mengoptimalkan keterbacaan dan keterpindaian bagi pengguna, formulir diberi batas maksimum 30 karakter, pada dasarnya mengatur lebar garis optik untuk label filter. Formulir ini menggunakan tata letak petak dan properti gap untuk memberi jarak pada {i>fieldset<i}.

form {
  display: grid;
  gap: 2ch;
  max-inline-size: 30ch;
}

Elemen <select>

Daftar label dan kotak centang menggunakan terlalu banyak ruang di perangkat seluler. Oleh karena itu, tata letak akan memeriksa perubahan perangkat penunjuk utama pengguna pengalaman sentuhan.

@media (pointer: coarse) {
  select[multiple] {
    display: block;
  }
}

Nilai coarse menunjukkan bahwa pengguna tidak akan dapat berinteraksi dengan layar dengan presisi tinggi menggunakan perangkat input utama mereka. Pada perangkat seluler, nilai pointer sering kali adalah coarse, sebagai interaksi utama adalah sentuhan. Di perangkat desktop, nilai pointer sering kali fine seperti yang umum menghubungkan {i>mouse<i} atau perangkat input presisi tinggi lainnya.

Kumpulan kolom

Gaya visual dan tata letak default <fieldset> dengan <legend> bersifat unik:

Screenshot gaya default untuk kumpulan kolom dan legenda.

Biasanya, untuk memberi jarak elemen turunan, saya akan menggunakan properti gap, tetapi posisi <legend> menyulitkan untuk membuat set yang berjarak sama anak-anak. Bukan gap, saudara yang berdekatan pemilih dan margin-block-start digunakan.

fieldset {
  padding: 2ch;

  & > div + div {
    margin-block-start: 2ch;
  }
}

Tindakan ini akan meniadakan <legend> penyesuaian ruang dengan menargetkan hanya <div> turunan.

Screenshot yang menunjukkan spasi margin antar-input, tetapi bukan legenda.

Label dan kotak centang filter

Sebagai turunan langsung dari <fieldset> dan dalam lebar maksimum formulir 30ch, teks label dapat digabungkan jika terlalu panjang. Membungkus teks itu bagus, tapi ketidaksejajaran antara teks dan kotak centang. Flexbox sangat cocok untuk hal ini.

fieldset > div {
  display: flex;
  gap: 2ch;
  align-items: baseline;
}
Screenshot yang menunjukkan bagaimana tanda centang disejajarkan
    baris pertama teks dalam skenario 
pembungkusan multi-baris.
Coba lebih banyak di Codepen ini

Petak animasi

Animasi tata letak dilakukan oleh Isotope. J performa tinggi dan andal untuk pengurutan dan filter interaktif.

JavaScript

Selain membantu mengatur animasi yang rapi, kisi interaktif, JavaScript digunakan untuk memoles beberapa tepian.

Menormalkan input pengguna

Desain ini memiliki satu bentuk dengan dua cara berbeda untuk memberikan input, dan mereka Jangan melakukan serialisasi hal yang sama. Dengan beberapa JavaScript, kita bisa menormalisasi data.

Screenshot konsol JavaScript DevTools yang
  menunjukkan tujuan, hasil data yang dinormalisasi.

Saya memilih untuk menyelaraskan struktur data elemen <select> dengan kotak centang yang dikelompokkan karena ada berbagai struktur penetapan harga. Untuk melakukannya, input ditambahkan ke elemen <select>. Pada titik ini, selectedOptions dipetakan.

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
  }, [])
})

Sekarang Anda dapat mengirimkan formulir ini, atau dalam kasus demo ini, kirimkan petunjuk kepada Isotope pada apa yang harus difilter.

Menyelesaikan elemen peran status

Elemen hanya menghitung dan mengumumkan jumlah filter berdasarkan kotak centang tetapi saya merasa itu adalah ide yang baik untuk juga berbagi jumlah dan memastikan pilihan elemen <select> juga dihitung.

Pilihan elemen <select> tercermin dalam counter()

Di bagian normalisasi data, pemroses sudah dibuat pada input. Di akhir {i>function<i} ini jumlah filter yang dipilih dan jumlah hasil untuk filter tersebut diketahui. Nilai dapat diteruskan ke elemen peran status seperti ini.

let statusRoleElement = document.querySelector('#applied-filters')
statusRoleElement.style.counterSet = selectData.length

Hasil ditampilkan dalam elemen role="status"

:checked menyediakan cara bawaan untuk meneruskan jumlah filter yang dipilih ke elemen peran status, tetapi tidak memiliki visibilitas terhadap jumlah hasil yang difilter. JavaScript dapat mengamati interaksi dengan kotak centang dan setelah memfilter petak, tambahkan textContent seperti yang dilakukan elemen <select>.

document
  .querySelector('aside form')
  .addEventListener('input', e => {
    // isotope demo code
    let filterResults = IsotopeGrid.getFilteredItemElements().length
    document.querySelector('#applied-filters').textContent = `giving ${filterResults} results`
})

Secara keseluruhan, pekerjaan ini menyelesaikan pengumuman "2 filter memberikan 25 hasil".

Screenshot pembaca layar MacOS yang mengumumkan hasil.

Sekarang pengalaman teknologi pendukung kami yang luar biasa akan dikirimkan ke semua pengguna, bagaimanapun mereka berinteraksi dengannya.

Kesimpulan

Sekarang setelah Anda tahu bagaimana saya melakukannya, bagaimana Anda akan 🙂

Mari kita diversifikasi pendekatan kami dan mempelajari semua cara untuk membangun di web. Buat demo, link tweet saya, dan saya akan menambahkannya ke bagian remix komunitas di bawah ini.

Remix komunitas

Belum ada apa-apa di sini.