Ringkasan dasar tentang cara membuat komponen multiselect yang responsif, adaptif, dan dapat diakses untuk pengalaman pengguna pengurutan dan pemfilteran.
Dalam postingan ini, saya ingin berbagi pemikiran tentang cara membuat komponen pilihan ganda. Coba demo.
Jika Anda lebih suka menonton video, berikut versi YouTube dari postingan ini:
Ringkasan
Pengguna sering kali disajikan dengan item, terkadang banyak item, dan dalam kasus ini, sebaiknya sediakan cara untuk mengurangi daftar agar tidak terjadi kelebihan pilihan. Postingan blog ini membahas UI pemfilteran sebagai cara untuk mengurangi pilihan. Hal ini dilakukan dengan menampilkan atribut item yang dapat dipilih atau dibatalkan pilihannya oleh pengguna, sehingga mengurangi hasil dan dengan demikian mengurangi kelebihan pilihan.
Interaksi
Tujuannya adalah memungkinkan penelusuran opsi filter yang cepat untuk semua pengguna dan berbagai jenis input mereka. Hal ini akan disampaikan dengan sepasang komponen yang adaptif dan responsif. Sidebar kotak centang tradisional untuk desktop, keyboard, dan pembaca layar, serta <select
multiple>
untuk pengguna sentuh.
Keputusan untuk menggunakan pilihan ganda bawaan untuk sentuhan, dan bukan untuk desktop, menghemat pekerjaan dan menciptakan pekerjaan, tetapi saya yakin memberikan pengalaman yang sesuai dengan lebih sedikit utang kode daripada membangun seluruh pengalaman responsif dalam satu komponen.
Sentuh
Komponen sentuh menghemat ruang dan membantu akurasi interaksi pengguna di perangkat seluler. Hal ini menghemat ruang dengan menciutkan seluruh sidebar kotak centang menjadi pengalaman sentuh overlay bawaan <select>
. Fitur ini membantu akurasi input dengan menampilkan pengalaman overlay sentuh besar yang disediakan oleh sistem.
Keyboard dan gamepad
Berikut adalah demonstrasi cara menggunakan <select multiple>
dari keyboard.
Multi-pilihan bawaan ini tidak dapat diberi gaya dan hanya ditawarkan dalam tata letak ringkas yang tidak cocok untuk menampilkan banyak opsi. Lihat bagaimana Anda tidak dapat benar-benar melihat berbagai opsi dalam kotak kecil tersebut? Meskipun Anda dapat mengubah ukurannya, kotak ini tetap tidak dapat digunakan seperti sidebar kotak centang.
Markup
Kedua komponen akan dimuat dalam elemen <form>
yang sama. Hasil dari
formulir ini, baik kotak centang maupun multi-pilihan, akan diamati dan digunakan untuk
memfilter petak, tetapi juga dapat dikirimkan ke server.
<form>
</form>
Komponen kotak centang
Grup kotak centang harus digabungkan dalam elemen
<fieldset>
dan diberi
<legend>
.
Jika HTML disusun dengan cara ini, pembaca layar dan
FormData akan
secara otomatis memahami hubungan elemen.
<form>
<fieldset>
<legend>New</legend>
… checkboxes …
</fieldset>
</form>
Setelah pengelompokan dilakukan, tambahkan <label>
dan <input type="checkbox">
untuk
setiap filter. Saya memilih untuk membungkusnya dalam <div>
sehingga properti CSS gap
dapat mengatur jaraknya secara merata dan mempertahankan perataan saat label menjadi beberapa baris.
<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>
komponen
Fitur elemen <select>
yang jarang digunakan adalah
multiple
.
Jika atribut digunakan dengan elemen <select>
, pengguna diizinkan untuk
memilih banyak item dari daftar. Ini seperti mengubah interaksi dari daftar pilihan
menjadi daftar kotak centang.
<form>
<select multiple="true" title="Filter results by category">
…
</select>
</form>
Untuk memberi label dan membuat grup di dalam <select>
, gunakan elemen
<optgroup>
dan berikan atribut serta nilai label
. Nilai elemen dan atribut ini
mirip dengan elemen <fieldset>
dan <legend>
.
<form>
<select multiple="true" title="Filter results by category">
<optgroup label="New">
…
</optgroup>
</select>
</form>
Sekarang tambahkan elemen
<option>
untuk 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>
Melacak input dengan penghitung untuk memberi tahu teknologi pendukung
Teknik status
role
digunakan dalam pengalaman pengguna ini, untuk melacak dan mempertahankan jumlah
filter untuk pembaca layar dan teknologi pendukung 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 perubahan yang dilakukan pada konten. Kita dapat memperbarui konten dengan penghitung CSS saat pengguna berinteraksi dengan kotak centang. Untuk melakukannya, pertama-tama kita perlu membuat penghitung dengan nama pada elemen induk dari elemen input dan status.
aside {
counter-reset: filters;
}
Secara default, jumlahnya adalah 0
, yang bagus, tidak ada yang :checked
secara
default dalam desain ini.
Selanjutnya, untuk menambah penghitung yang baru dibuat, kita akan menargetkan turunan elemen
<aside>
yang :checked
. Saat pengguna mengubah status input,
penghitung filters
akan bertambah.
aside :checked {
counter-increment: filters;
}
CSS kini mengetahui jumlah umum UI kotak centang dan elemen peran status
kosong dan menunggu nilai. Karena CSS mempertahankan jumlah dalam
memori, fungsi
counter()
memungkinkan akses ke nilai dari konten pseudo
elemen:
aside #applied-filters::before {
content: counter(filters) " filters ";
}
HTML untuk elemen peran status kini akan mengumumkan "2 filter " ke pembaca layar. Ini adalah awal yang baik, tetapi kita bisa melakukan yang lebih baik, seperti membagikan jumlah hasil yang telah diperbarui oleh filter. Kita akan melakukan pekerjaan ini dari JavaScript, karena di luar kemampuan penghitung.
Menyimpan kegembiraan
Algoritma penghitung terasa sangat baik dengan CSS nesting-1, karena saya dapat 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 di antara kedua komponen. Sebagian besar gaya tata letak ditujukan untuk komponen kotak centang desktop.
Formulir
Untuk mengoptimalkan keterbacaan dan keterpindaian bagi pengguna, formulir diberi lebar maksimum 30 karakter, yang pada dasarnya menetapkan lebar baris optik untuk setiap label filter. Formulir menggunakan tata letak petak dan properti gap
untuk mengatur jarak
fieldset.
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 memeriksa perangkat penunjuk utama pengguna untuk mengubah pengalaman sentuh.
@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. Di perangkat seluler, nilai pointer sering kali coarse
, karena interaksi utama adalah sentuhan. Pada perangkat desktop, nilai pointer sering kali fine
karena biasanya
ada mouse atau perangkat input presisi tinggi lainnya yang terhubung.
Fieldset
Gaya dan tata letak default <fieldset>
dengan <legend>
bersifat unik:
Biasanya, untuk mengatur jarak elemen turunan, saya akan menggunakan properti gap
, tetapi pemosisian <legend>
yang unik membuat sulit untuk membuat sekumpulan turunan dengan jarak yang sama. Sebagai pengganti gap
, pemilih elemen saudara berdekatan dan
margin-block-start
digunakan.
fieldset {
padding: 2ch;
& > div + div {
margin-block-start: 2ch;
}
}
Tindakan ini akan membuat <legend>
tidak menyesuaikan ruangnya dengan hanya menargetkan turunan
<div>
.
Label dan kotak centang filter
Sebagai turunan langsung dari <fieldset>
dan dalam lebar maksimum
30ch
formulir, teks label dapat dilipat jika terlalu panjang. Melakukan penggabungan teks adalah hal yang bagus, tetapi
ketidakselarasan antara teks dan kotak centang tidak bagus. Flexbox sangat cocok untuk hal ini.
fieldset > div {
display: flex;
gap: 2ch;
align-items: baseline;
}

Petak animasi
Animasi tata letak dilakukan oleh Isotope. Plugin berperforma tinggi dan andal untuk pengurutan dan pemfilteran interaktif.
JavaScript
Selain membantu mengatur tata letak petak interaktif beranimasi yang rapi, JavaScript digunakan untuk memoles beberapa bagian yang kurang sempurna.
Menormalisasi input pengguna
Desain ini memiliki satu formulir dengan dua cara berbeda untuk memberikan input, dan keduanya tidak menserialisasi yang sama. Namun, dengan beberapa JavaScript, kita dapat menormalisasi data.
Saya memilih untuk menyelaraskan struktur data elemen <select>
dengan struktur kotak centang yang dikelompokkan. Untuk melakukannya, pemroses peristiwa
input
ditambahkan ke elemen <select>
, yang pada saat itu
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 dengan aman, atau dalam kasus demo ini, menginstruksikan Isotope tentang apa yang akan difilter.
Menyelesaikan elemen peran status
Elemen hanya menghitung dan mengumumkan jumlah filter berdasarkan interaksi kotak centang, tetapi saya merasa ada baiknya juga membagikan jumlah hasil dan memastikan pilihan elemen <select>
juga dihitung.
Pilihan elemen <select>
tercermin dalam counter()
Di bagian normalisasi data, pemroses sudah dibuat pada input. Di akhir fungsi 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 yang tercermin dalam elemen role="status"
:checked
menyediakan cara bawaan untuk meneruskan jumlah filter yang dipilih ke
elemen peran status, tetapi tidak memiliki visibilitas ke jumlah hasil yang difilter.
JavaScript dapat memantau interaksi dengan kotak centang dan setelah memfilter
petak, menambahkan 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 melengkapi pengumuman "2 filter memberikan 25 hasil".
Sekarang, pengalaman teknologi pendukung yang sangat baik akan diberikan kepada semua pengguna, apa pun cara mereka berinteraksi dengannya.
Kesimpulan
Sekarang setelah Anda tahu cara saya melakukannya, bagaimana Anda‽ 🙂
Mari kita diversifikasi pendekatan kita dan pelajari semua cara untuk membangun di web. Buat demo, tweet linknya kepada saya, dan saya akan menambahkannya ke bagian remix komunitas di bawah.
Remix komunitas
Belum ada apa-apa di sini.