Membangun Sistem Menu yang Dapat Diakses
Diterbitkan: 2022-03-10Catatan Editor : Artikel ini awalnya muncul di Komponen Inklusif. Jika Anda ingin tahu lebih banyak tentang artikel komponen inklusif serupa, ikuti @inclusicomps di Twitter atau berlangganan umpan RSS. Dengan mendukung inklusif-components.design di Patreon, Anda dapat membantu menjadikannya basis data paling komprehensif dari komponen antarmuka tangguh yang tersedia.
Klasifikasi itu sulit. Ambil kepiting, misalnya. Kepiting hermit, kepiting porselen, dan kepiting tapal kuda bukanlah — secara taksonomi — kepiting sejati . Tapi itu tidak menghentikan kita menggunakan akhiran "kepiting". Ini menjadi lebih membingungkan ketika, dari waktu ke waktu dan berkat proses yang disebut karsinisasi , kepiting yang tidak benar berevolusi untuk lebih menyerupai kepiting sejati. Ini adalah kasus kepiting raja, yang diyakini sebagai kepiting pertapa di masa lalu. Bayangkan ukuran cangkang mereka!
Dalam desain, kita sering membuat kesalahan yang sama dengan memberi nama yang sama untuk hal yang berbeda. Mereka tampak serupa, tetapi penampilan bisa menipu. Ini dapat memiliki efek yang tidak menguntungkan pada kejelasan pustaka komponen Anda. Dalam hal penyertaan, ini juga dapat mengarahkan Anda untuk menggunakan kembali komponen yang tidak sesuai secara semantik dan perilaku. Pengguna akan mengharapkan satu hal dan mendapatkan yang lain.
Istilah "dropdown" merupakan contoh klasik. Banyak hal "turun turun" di antarmuka, termasuk kumpulan <option>
dari elemen <select>
, dan daftar tautan yang diungkapkan JavaScript yang membentuk submenu navigasi. Nama yang sama; hal yang cukup berbeda. (Beberapa orang menyebut ini "pulldown", tentu saja, tetapi jangan membahasnya.)
Dropdown yang merupakan serangkaian opsi sering disebut "menu", dan saya ingin membicarakannya di sini. Kami akan merancang menu yang sebenarnya , tetapi ada banyak yang bisa dikatakan tentang menu yang tidak benar-benar benar di sepanjang jalan.
Mari kita mulai dengan kuis. Apakah kotak tautan yang menggantung dari bilah navigasi dalam ilustrasi merupakan menu?
Jawabannya tidak, bukan menu yang sebenarnya.
Ini adalah konvensi lama bahwa skema navigasi terdiri dari daftar tautan. Sebuah konvensi yang hampir sama menyatakan bahwa sub-navigasi harus disediakan sebagai daftar tautan bersarang . Jika saya menghapus CSS untuk komponen yang diilustrasikan di atas, saya akan melihat sesuatu seperti berikut, kecuali berwarna biru dan di Times New Roman.
Secara semantik, daftar tautan bersarang benar dalam konteks ini. Sistem navigasi sebenarnya adalah daftar isi dan beginilah cara daftar isi disusun. Satu-satunya hal yang benar-benar membuat kita berpikir "menu" adalah gaya daftar bersarang dan cara daftar itu ditampilkan saat mengarahkan kursor atau fokus.
Di situlah beberapa kesalahan dan mulai menambahkan semantik WAI-ARIA: aria-haspopup="true"
, role="menu"
, role="menuitem"
dll. Ada tempat untuk ini, seperti yang akan kita bahas, tetapi tidak di sini . Berikut adalah dua alasan mengapa:
- Menu ARIA tidak ditujukan untuk navigasi tetapi untuk perilaku aplikasi. Bayangkan sistem menu untuk aplikasi desktop.
- Tautan tingkat atas harus dapat digunakan sebagai tautan , artinya tidak berperilaku seperti tombol menu.
Mengenai (2): Saat melintasi wilayah navigasi dengan submenu, orang akan mengharapkan setiap submenu muncul saat mengarahkan atau memfokuskan tautan "tingkat atas" ("Toko" dalam ilustrasi). Ini mengungkapkan submenu dan menempatkan tautannya sendiri dalam urutan fokus. Dengan sedikit bantuan dari JavaScript yang menangkap fokus dan peristiwa blur untuk mempertahankan tampilan submenu saat diperlukan, seseorang yang menggunakan keyboard harus dapat membuka tab melalui setiap tautan dari setiap tingkatan, secara bergantian.
Tombol menu yang menggunakan properti aria-haspopup="true"
tidak berperilaku seperti ini. Mereka diaktifkan pada klik dan tidak memiliki tujuan lain selain untuk mengungkapkan menu yang disekresikan.
Seperti yang digambarkan, apakah menu itu terbuka atau tertutup harus dikomunikasikan dengan aria-expanded
. Anda seharusnya hanya mengubah status ini pada klik, bukan pada fokus. Pengguna biasanya tidak mengharapkan perubahan status secara eksplisit pada peristiwa fokus belaka. Dalam sistem navigasi kami, status tidak benar-benar berubah; itu hanya trik penataan. Secara perilaku, kita dapat Tab melalui navigasi seolah-olah tidak ada tipuan show/hide seperti itu yang terjadi.
Masalah Dengan Submenu Navigasi
Submenu navigasi (atau "dropdown" untuk beberapa) bekerja dengan baik dengan mouse atau keyboard, tetapi tidak terlalu panas saat disentuh. Saat Anda menekan tautan "Toko" tingkat atas dalam contoh kami untuk pertama kalinya, Anda memintanya untuk membuka submenu dan mengikuti tautan.
Ada dua kemungkinan resolusi di sini:
- Cegah perilaku default tautan tingkat atas (
e.preventDefault()
) dan skrip dalam semantik dan perilaku menu WAI-ARIA lengkap. - Pastikan setiap halaman tujuan tingkat atas memiliki daftar isi sebagai alternatif submenu.
(1) tidak memuaskan karena, seperti yang saya sebutkan sebelumnya, semantik dan perilaku semacam ini tidak diharapkan dalam konteks ini, di mana tautan adalah kontrol subjek. Plus pengguna tidak bisa lagi menavigasi ke halaman tingkat atas, jika ada.
Sidenote: Perangkat mana yang merupakan perangkat sentuh?
Sangat menggoda untuk berpikir, "ini bukan solusi yang bagus, tapi saya hanya akan menambahkannya untuk antarmuka sentuh". Masalahnya adalah: bagaimana cara mendeteksi jika perangkat memiliki layar sentuh?
Anda tentu tidak boleh menyamakan "layar kecil" dengan "sentuh diaktifkan". Setelah bekerja di kantor yang sama dengan orang-orang yang membuat layar sentuh untuk museum, saya dapat meyakinkan Anda bahwa beberapa layar terbesar di sekitar adalah layar sentuh. Keyboard ganda dan laptop input sentuh juga menjadi semakin produktif.
Dengan cara yang sama, banyak tetapi tidak semua perangkat yang lebih kecil adalah perangkat sentuh. Dalam desain inklusif, Anda tidak boleh membuat asumsi.
Resolusi (2) lebih inklusif dan kuat karena memberikan "pengunduran" bagi pengguna dari semua input. Tapi kutipan ketakutan di sekitar istilah fallback di sini cukup disengaja karena menurut saya tabel konten dalam halaman adalah cara yang lebih baik untuk menyediakan navigasi.
Tim Layanan Digital Pemerintah pemenang penghargaan tampaknya setuju. Anda mungkin juga pernah melihatnya di Wikipedia.
Daftar Isi
Daftar isi adalah navigasi untuk halaman atau bagian halaman terkait dan harus serupa secara semantik dengan wilayah navigasi situs utama, menggunakan elemen <nav>
, daftar, dan mekanisme pelabelan grup.
<nav aria-labelledby="sections-heading"> <h2>Products</h2> <ul> <li><a href="/products/dog-costumes">Dog costumes</a></li> <li><a href="/products/waffle-irons">Waffle irons</a></li> <li><a href="/products/magical-orbs">Magical orbs</a></li> </ul> </nav> <!-- each section, in order, here -->
Catatan
- Dalam contoh ini, kami membayangkan bahwa setiap bagian adalah halamannya sendiri, seperti yang ada di submenu tarik-turun.
- Penting bahwa setiap halaman "Toko" ini memiliki struktur yang sama, dengan daftar isi "Produk" ini ada di tempat yang sama. Konsistensi mendukung pemahaman.
- Daftar mengelompokkan item dan menghitungnya dalam keluaran teknologi bantu, seperti suara sintetis pembaca layar.
-
<nav>
diberi label secara rekursif oleh heading menggunakanaria-labelledby
. Ini berarti "navigasi produk" akan diumumkan di sebagian besar pembaca layar saat memasuki wilayah oleh Tab . Ini juga berarti bahwa "navigasi produk" akan diperinci dalam antarmuka elemen pembaca layar, dari mana pengguna dapat menavigasi ke wilayah secara langsung.
Semua dalam satu halaman
Jika Anda dapat memasukkan semua bagian ke dalam satu halaman tanpa menjadi terlalu panjang dan sulit untuk digulir, lebih baik lagi. Cukup tautkan ke pengidentifikasi hash setiap bagian. Misalnya, href="#waffle-irons"
harus menunjuk ke .
<nav aria-labelledby="sections-heading"> <h2>Products</h2> <ul> <li><a href="#dog-costumes">Dog costumes</a></li> <li><a href="#waffle-irons">Waffle irons</a></li> <li><a href="#magical-orbs">Magical orbs</a></li> </ul> </nav> <!-- dog costumes section here --> <section tabindex="-1"> <h2>Waffle Irons</h2> </section> <!-- magical orbs section here -->
( Catatan: Beberapa browser buruk dalam mengirimkan fokus ke fragmen halaman tertaut. Menempatkan tabindex="-1"
pada fragmen target memperbaikinya.)
Di mana sebuah situs memiliki banyak konten, arsitektur informasi yang dibangun dengan hati-hati, diekspresikan melalui penggunaan "menu" daftar isi secara bebas jauh lebih disukai daripada sistem dropdown yang genting dan berat. Tidak hanya lebih mudah untuk membuat responsif, dan membutuhkan lebih sedikit kode untuk melakukannya, tetapi juga membuat segalanya lebih jelas: di mana sistem dropdown menyembunyikan struktur, tabel konten membuatnya terbuka.
Beberapa situs, termasuk gov.uk Government Digital Service, menyertakan halaman indeks (atau "topik") yang hanya berupa daftar isi. Ini adalah konsep yang sangat kuat sehingga generator situs statis populer Hugo menghasilkan halaman seperti itu secara default.
Arsitektur informasi adalah bagian besar dari inklusi. Situs yang tidak terorganisir dengan baik dapat secara teknis sesuai dengan keinginan Anda, tetapi masih akan mengasingkan banyak pengguna — terutama mereka yang memiliki gangguan kognitif atau mereka yang terdesak waktu.
Tombol Menu Navigasi
Sementara kita berada di subjek menu yang berhubungan dengan navigasi palsu, akan lalai dari saya untuk tidak berbicara tentang tombol menu navigasi. Anda hampir pasti pernah melihat ini dilambangkan dengan ikon "hamburger" atau "navicon" tiga baris.
Bahkan dengan arsitektur informasi yang disederhanakan dan hanya satu tingkat tautan navigasi, ruang di layar kecil sangat mahal. Menyembunyikan navigasi di balik tombol berarti ada lebih banyak ruang untuk konten utama di area pandang.
Tombol navigasi adalah hal terdekat yang telah kami pelajari sejauh ini dengan tombol menu yang sebenarnya . Karena memiliki tujuan untuk mengubah ketersediaan menu saat diklik, seharusnya:
- Identifikasi dirinya sebagai tombol, bukan tautan;
- Identifikasi status yang diperluas atau diciutkan dari menu yang sesuai (yang, dalam istilah yang ketat, hanyalah daftar tautan).
Peningkatan progresif
Tapi mari kita tidak maju dari diri kita sendiri. Kita harus memperhatikan peningkatan progresif dan mempertimbangkan bagaimana ini akan bekerja tanpa JavaScript.
Dalam dokumen HTML yang tidak disempurnakan, tidak banyak yang dapat Anda lakukan dengan tombol (kecuali tombol kirim tetapi itu bahkan tidak terkait erat dengan apa yang ingin kami capai di sini). Alih-alih, mungkin kita harus memulai hanya dengan tautan yang membawa kita ke navigasi?
<a href="#navigation">navigation</a> <!-- some content here perhaps --> <nav> <ul> <li><a href="/">Home</a></li> <li><a href="/about">About</a></li> <li><a href="/shop">Shop</a></li> <li><a href="/content">Content</a></li> </ul> </nav>
Tidak ada gunanya memiliki tautan kecuali ada banyak konten antara tautan dan navigasi. Karena navigasi situs hampir selalu muncul di dekat bagian atas urutan sumber, tidak perlu. Jadi, sungguh, menu navigasi tanpa JavaScript seharusnya… beberapa navigasi.
<nav> <ul> <li><a href="/">Home</a></li> <li><a href="/about">About</a></li> <li><a href="/shop">Shop</a></li> <li><a href="/content">Content</a></li> </ul> </nav>
Anda meningkatkan ini dengan menambahkan tombol, dalam keadaan awalnya, dan menyembunyikan navigasi (menggunakan atribut hidden
):
<nav> <button aria-expanded="false">Menu</button> <ul hidden> <li><a href="/">Home</a></li> <li><a href="/about">About</a></li> <li><a href="/shop">Shop</a></li> <li><a href="/contact">Contact</a></li> </ul> </nav>
Beberapa browser lama — Anda tahu yang mana — tidak mendukung hidden
, jadi ingatlah untuk memasukkan yang berikut ini ke dalam CSS Anda. Ini memperbaiki masalah karena display: none
yang memiliki pengaruh yang sama untuk menyembunyikan menu dari teknologi bantu dan menghapus tautan dari urutan fokus.
[hidden] { display: none; }
Melakukan yang terbaik untuk mendukung perangkat lunak yang lebih lama, tentu saja, merupakan tindakan desain yang inklusif. Beberapa tidak dapat atau tidak mau meng-upgrade.
Penempatan
Di mana banyak orang salah adalah dengan menempatkan tombol di luar wilayah. Ini berarti pengguna pembaca layar yang berpindah ke <nav>
menggunakan pintasan akan menganggapnya kosong, yang tidak terlalu membantu. Dengan daftar yang disembunyikan dari pembaca layar, mereka baru saja menemukan ini:
<nav> </nav>
Inilah cara kita dapat beralih status:
var navButton = document.querySelector('nav button'); navButton.addEventListener('click', function() { let expanded = this.getAttribute('aria-expanded') === 'true' || false; this.setAttribute('aria-expanded', !expanded); let menu = this.nextElementSibling; menu.hidden = !menu.hidden; });
aria-kontrol
Seperti yang saya tulis di Aria-controls Is Poop, atribut aria-controls
, yang dimaksudkan untuk membantu pengguna pembaca layar menavigasi dari elemen pengontrol ke elemen terkontrol, hanya didukung di pembaca layar JAWS. Jadi Anda tidak bisa mengandalkannya begitu saja.
Tanpa metode yang baik untuk mengarahkan pengguna antar elemen, Anda harus memastikan salah satu dari berikut ini benar:
- Tautan pertama daftar yang diperluas adalah berikutnya dalam urutan fokus setelah tombol (seperti pada contoh kode sebelumnya).
- Tautan pertama difokuskan secara terprogram saat membuka daftar.
Dalam hal ini, saya akan merekomendasikan (1). Ini jauh lebih sederhana karena Anda tidak perlu khawatir tentang memindahkan fokus kembali ke tombol dan pada acara mana untuk melakukannya. Selain itu, saat ini tidak ada yang memperingatkan pengguna bahwa fokus mereka akan dipindahkan ke tempat yang berbeda. Dalam menu sebenarnya yang akan kita bahas segera, ini adalah tugas dari aria-haspopup="true"
.
Mempekerjakan aria-controls
tidak terlalu merugikan, kecuali bahwa itu membuat pembacaan di pembaca layar lebih bertele-tele. Namun, beberapa pengguna JAWS mungkin mengharapkannya. Inilah cara penerapannya, menggunakan id
daftar sebagai sandi:
<nav> <button aria-expanded="false" aria-controls="menu-list">Menu</button> <ul hidden> <li><a href="/">Home</a></li> <li><a href="/about">About</a></li> <li><a href="/shop">Shop</a></li> <li><a href="/contact">Contact</a></li> </ul> </nav>
Peran menu dan item menu
Menu yang sebenarnya (dalam pengertian WAI-ARIA) harus mengidentifikasi dirinya seperti itu menggunakan peran menu
(untuk wadah) dan, biasanya, menuitem
menu anak-anak (peran anak lain mungkin berlaku). Peran orang tua dan anak ini bekerja sama untuk memberikan informasi kepada teknologi bantu. Inilah cara daftar dapat ditambah untuk memiliki semantik menu:
<ul role="menu"> <li role="menuitem">Item 1</li> <li role="menuitem">Item 2</li> <li role="menuitem">Item 3</li> </ul>
Karena menu navigasi kita mulai berperilaku seperti menu “benar”, bukankah ini seharusnya ada?
Jawaban singkatnya adalah: tidak. Jawaban panjangnya adalah: tidak, karena item daftar kami berisi tautan dan elemen menuitem
tidak dimaksudkan untuk memiliki turunan interaktif. Artinya, mereka adalah kontrol dalam menu.
Kita tentu saja dapat menekan daftar semantik <li>
menggunakan role="presentation"
atau role="none"
(yang setara) dan menempatkan peran menuitem
pada setiap tautan. Namun, ini akan menekan peran tautan implisit. Dengan kata lain, contoh yang akan diikuti akan diumumkan sebagai “Beranda, item menu”, bukan “Beranda, tautan” atau “Beranda, item menu, tautan”. Peran ARIA hanya menggantikan peran HTML.
<!-- will be read as "Home, menu item" --> <li role="presentation"> <a href="/" role="menuitem">Home</a> </li>
Kami ingin pengguna mengetahui bahwa mereka menggunakan tautan dan dapat mengharapkan perilaku tautan, jadi ini tidak baik. Seperti yang saya katakan, menu sebenarnya adalah untuk perilaku aplikasi (didorong JavaScript).
Apa yang tersisa dari kami adalah sejenis komponen hibrida, yang bukan menu yang sebenarnya, tetapi setidaknya memberi tahu pengguna apakah daftar tautan terbuka, berkat status aria-expanded
. Ini adalah pola yang sangat memuaskan untuk menu navigasi.
Sidenote: Elemen <select>
Jika Anda telah terlibat dalam desain responsif sejak awal, Anda mungkin ingat pola di mana navigasi diringkas menjadi elemen <select>
untuk viewports sempit.
Seperti tombol sakelar berbasis kotak centang yang telah kita diskusikan, menggunakan elemen asli yang berperilaku seperti yang dimaksudkan tanpa skrip tambahan adalah pilihan yang baik untuk efisiensi dan — terutama pada kinerja seluler. Dan elemen <select>
adalah semacam menu, dengan semantik yang mirip dengan menu yang dipicu tombol yang akan segera kita buat.
Namun, seperti halnya tombol sakelar kotak centang, kami menggunakan elemen yang terkait dengan memasukkan input, bukan sekadar membuat pilihan. Ini mungkin menyebabkan kebingungan bagi banyak pengguna — terutama karena pola ini menggunakan JavaScript untuk membuat <option>
yang dipilih berperilaku seperti tautan. Perubahan konteks yang tidak terduga yang ditimbulkan ini dianggap sebagai kegagalan menurut kriteria 3.2.2 Pada Input (Level A) WCAG.
Menu Sejati
Sekarang kita telah membahas tentang menu palsu dan menu semu, saatnya telah tiba untuk membuat menu yang sebenarnya , seperti yang dibuka dan ditutup oleh tombol menu yang sebenarnya. Dari sini saya akan merujuk ke tombol dan menu bersama-sama hanya sebagai "tombol menu".
Tetapi dalam hal apa tombol menu kita benar? Yah, itu akan menjadi komponen menu yang dimaksudkan untuk memilih opsi dalam aplikasi subjek, yang mengimplementasikan semua semantik yang diharapkan dan perilaku yang sesuai untuk dianggap konvensional untuk alat semacam itu.
Seperti yang sudah disebutkan, konvensi ini berasal dari desain aplikasi desktop. Atribusi ARIA dan manajemen fokus yang diatur JavaScript diperlukan untuk menirunya sepenuhnya. Bagian dari tujuan ARIA adalah untuk membantu pengembang web menciptakan pengalaman web yang kaya tanpa melanggar konvensi kegunaan yang dibuat di dunia asli.
Dalam contoh ini, kita akan membayangkan aplikasi kita adalah semacam permainan atau kuis. Tombol menu kami akan membiarkan pengguna memilih tingkat kesulitan. Dengan semua semantik di tempat, menu terlihat seperti ini:
<button aria-haspopup="true" aria-expanded="false"> Difficulty <span aria-hidden="true">▾</span> </button> <div role="menu"> <button role="menuitem">Easy</button> <button role="menuitem">Medium</button> <button role="menuitem">Incredibly Hard</button> </div>
Catatan
- Properti
aria-haspopup
hanya menunjukkan bahwa tombol mengeluarkan menu. Ini bertindak sebagai peringatan bahwa, ketika ditekan, pengguna akan dipindahkan ke menu "popup" (kami akan segera membahas perilaku fokus). Nilainya tidak berubah — tetaptrue
setiap saat. -
<span>
di dalam tombol berisi titik unicode untuk segitiga kecil hitam yang mengarah ke bawah. Konvensi ini menunjukkan secara visual apa yang dilakukanaria-haspopup
secara non-visual — bahwa menekan tombol akan menampilkan sesuatu di bawahnya. Atribusiaria-hidden="true"
mencegah pembaca layar mengumumkan "segitiga mengarah ke bawah" atau yang serupa. Berkataria-haspopup
, itu tidak diperlukan dalam konteks non-visual. - Properti
aria-haspopup
dilengkapi denganaria-expanded
. Ini memberi tahu pengguna apakah menu saat ini dalam keadaan terbuka (diperluas) atau ditutup (diciutkan) dengan beralih antara nilaitrue
danfalse
. - Menu itu sendiri mengambil peran
menu
(bernama tepat). Dibutuhkan keturunan dengan peranmenuitem
. Mereka tidak perlu menjadi anak-anak langsung dari elemenmenu
, tetapi mereka dalam kasus ini — untuk kesederhanaan.
Keyboard Dan Perilaku Fokus
Dalam hal membuat keyboard kontrol interaktif dapat diakses, hal terbaik yang dapat Anda lakukan adalah menggunakan elemen yang tepat. Karena kami menggunakan elemen <button>
di sini, kami dapat yakin bahwa peristiwa klik akan diaktifkan pada penekanan tombol Enter dan Spasi , seperti yang ditentukan dalam antarmuka HTMLButtonElement. Ini juga berarti bahwa kita dapat menonaktifkan item menu menggunakan properti disabled
terkait tombol.
Ada lebih banyak interaksi tombol menu keyboard. Berikut ringkasan dari semua fokus dan perilaku keyboard yang akan kita terapkan, berdasarkan pada WAI-ARIA Authoring Practices 1.1:
Masuk , Spasi atau pada tombol menu | Membuka menu |
pada item menu | Memindahkan fokus ke item menu berikutnya, atau item menu pertama jika Anda berada di item menu terakhir |
pada item menu | Memindahkan fokus ke item menu sebelumnya, atau item menu terakhir jika Anda berada di item menu pertama |
pada tombol menu | Menutup menu jika terbuka |
Esc pada item menu | Menutup menu dan memfokuskan tombol menu |
Keuntungan memindahkan fokus antar item menu menggunakan tombol panah adalah Tab dipertahankan untuk keluar dari menu. Dalam praktiknya, ini berarti pengguna tidak perlu menelusuri setiap item menu untuk keluar dari menu — peningkatan besar untuk kegunaan, terutama jika ada banyak item menu.
Penerapan tabindex="-1"
membuat item menu tidak dapat difokuskan oleh Tab tetapi mempertahankan kemampuan untuk memfokuskan elemen secara terprogram, setelah menangkap penekanan tombol pada tombol panah.
<button aria-haspopup="true" aria-expanded="false"> Difficulty <span aria-hidden="true">▾</span> </button> <div role="menu"> <button role="menuitem" tabindex="-1">Easy</button> <button role="menuitem" tabindex="-1">Medium</button> <button role="menuitem" tabindex="-1">Incredibly Hard</button> </div>
Metode terbuka
Sebagai bagian dari desain API yang baik, kita dapat membuat metode untuk menangani berbagai peristiwa.
Misalnya, metode open
perlu mengubah nilai aria-expanded
menjadi “true”, mengubah properti tersembunyi menu menjadi false
, dan memfokuskan menuitem
menu pertama di menu yang tidak dinonaktifkan:
MenuButton.prototype.open = function () { this.button.setAttribute('aria-expanded', true); this.menu.hidden = false; this.menu.querySelector(':not(\[disabled])').focus(); return this; }
Kami dapat menjalankan metode ini di mana pengguna menekan tombol bawah pada contoh tombol menu terfokus:
this.button.addEventListener('keydown', function (e) { if (e.keyCode === 40) { this.open(); } }.bind(this));
Selain itu, pengembang yang menggunakan skrip ini sekarang dapat membuka menu secara terprogram:
exampleMenuButton = new MenuButton(document.querySelector('\[aria-haspopup]')); exampleMenuButton.open();
Sidenote: Peretasan kotak centang
Sebisa mungkin, lebih baik tidak menggunakan JavaScript kecuali Anda perlu. Melibatkan teknologi ketiga di atas HTML dan CSS tentu saja meningkatkan kompleksitas dan kerapuhan sistemik. Namun, tidak semua komponen dapat dibangun dengan memuaskan tanpa JavaScript dalam campurannya.
Dalam kasus tombol menu, antusiasme untuk membuatnya "berfungsi tanpa JavaScript" telah menyebabkan sesuatu yang disebut peretasan kotak centang. Di sinilah keadaan dicentang (atau tidak dicentang) dari kotak centang tersembunyi digunakan untuk mengaktifkan visibilitas elemen menu menggunakan CSS.
/* menu closed */ [type="checkbox"] + [role="menu"] { display: none; } /* menu open */ [type="checkbox"]:checked + [role="menu"] { display: block; }
Untuk pengguna pembaca layar, peran kotak centang dan status yang dicentang tidak masuk akal dalam konteks ini. Ini sebagian dapat diatasi dengan menambahkan role="button"
ke kotak centang.
<input type="checkbox" role="button" aria-haspopup="true">
Sayangnya, ini menekan komunikasi status tercentang implisit, membuat kami kehilangan umpan balik status bebas-JavaScript (miskin meskipun akan "diperiksa" dalam konteks ini).
Tetapi dimungkinkan untuk menipu aria-expanded
. Kami hanya perlu menyediakan label kami dengan dua bentang seperti di bawah ini.
<input type="checkbox" role="button" aria-haspopup="true" class="vh"> <label for="toggle" data-opens-menu> Difficulty <span class="vh expanded-text">expanded</span> <span class="vh collapsed-text">collapsed</span> <span aria-hidden="true">▾</span> </label>
Keduanya disembunyikan secara visual menggunakan kelas visually-hidden
, tetapi — bergantung pada status kita saat ini — hanya satu yang disembunyikan untuk pembaca layar juga. Artinya, hanya satu yang memiliki display: none
, dan ini ditentukan oleh status centang yang masih ada (tetapi tidak dikomunikasikan):
/* class to hide spans visually */ .vh { position: absolute !important; clip: rect(1px, 1px, 1px, 1px); padding: 0 !important; border: 0 !important; height: 1px !important; width: 1px !important; overflow: hidden; } /* reveal the correct state wording to screen readers based on state */ [type="checkbox"]:checked + label .expanded-text { display: inline; } [type="checkbox"]:checked + label .collapsed-text { display: none; } [type="checkbox"]:not(:checked) + label .expanded-text { display: none; } [type="checkbox"]:not(:checked) + label .collapsed-text { display: inline; }
Ini pintar dan semuanya, tetapi tombol menu kami masih belum lengkap karena perilaku fokus yang diharapkan yang telah kami diskusikan tidak dapat diimplementasikan tanpa JavaScript.
Perilaku ini konvensional dan diharapkan, membuat tombol lebih bermanfaat. Namun, jika Anda benar-benar perlu menerapkan tombol menu tanpa JavaScript, ini sedekat yang Anda bisa dapatkan. Mengingat tombol menu navigasi cut-down yang saya bahas sebelumnya menawarkan konten menu yang tidak bergantung pada JavaScript itu sendiri (yaitu tautan), pendekatan ini mungkin merupakan opsi yang sesuai.
Untuk bersenang-senang, inilah codePen yang mengimplementasikan tombol menu navigasi bebas JavaScript.
Lihat contoh tombol menu Pen Navigation no JS by Heydon (@heydon) di CodePen.
( Catatan: Hanya Spasi yang membuka menu.)
Acara "pilih"
Mengeksekusi beberapa metode harus memancarkan peristiwa sehingga kami dapat menyiapkan pendengar. Misalnya, kita dapat memancarkan acara choose
ketika pengguna mengklik item menu. Kita dapat mengatur ini menggunakan CustomEvent
, yang memungkinkan kita meneruskan argumen ke properti detail
acara. Dalam hal ini, argumen ("pilihan") akan menjadi simpul DOM item menu yang dipilih.
MenuButton.prototype.choose = function (choice) { // Define the 'choose' event var chooseEvent = new CustomEvent('choose', { detail: { choice: choice } }); // Dispatch the event this.button.dispatchEvent(chooseEvent); return this; }
Ada banyak hal yang bisa kita lakukan dengan mekanisme ini. Mungkin kami memiliki wilayah langsung yang disiapkan dengan id
menuFeedback
:
<div role="alert"></div>
Sekarang kita dapat mengatur pendengar dan mengisi wilayah langsung dengan informasi yang disekresikan di dalam acara:
exampleMenuButton.addEventListener('choose', function (e) { // Get the node's text content (label) var choiceLabel = e.details.choice.textContent; // Get the live region node var liveRegion = document.getElementById('menuFeedback'); // Populate the live region liveRegion.textContent = 'Your difficulty level is ${choiceLabel}'; });
Saat item menu dipilih, pengguna pembaca layar akan mendengar, “Anda memilih [label item menu]” . Wilayah langsung (didefinisikan di sini dengan atribusi role=“alert”
) mengumumkan kontennya di pembaca layar setiap kali konten itu berubah. Wilayah langsung tidak wajib, tetapi ini adalah contoh dari apa yang mungkin terjadi di antarmuka sebagai respons terhadap pengguna yang membuat pilihan menu.
Pilihan Tetap
Tidak semua item menu untuk memilih pengaturan tetap. Banyak yang hanya bertindak seperti tombol standar yang membuat sesuatu di antarmuka terjadi saat ditekan. Namun, dalam kasus tombol menu kesulitan kami, kami ingin menunjukkan yang merupakan pengaturan kesulitan saat ini — yang dipilih terakhir.
Atribut aria-checked="true"
berfungsi untuk item yang, alih-alih menuitem
, mengambil peran menuitemradio
. Markup yang ditingkatkan, dengan item kedua dicentang ( set ) terlihat seperti ini:
<button aria-haspopup="true" aria-expanded="false"> Difficulty <span aria-hidden="true">▾</span> </button> <div role="menu"> <button role="menuitemradio" tabindex="-1">Easy</button> <button role="menuitemradio" aria-checked="true" tabindex="-1">Medium</button> <button role="menuitemradio" tabindex="-1">Incredibly Hard</button> </div>
Menu asli di banyak platform menunjukkan item yang dipilih menggunakan tanda centang. Kita dapat melakukannya tanpa kesulitan menggunakan sedikit CSS tambahan:
[role="menuitem"] [aria-checked="true"]::before { content: '\2713\0020'; }
Saat melintasi menu dengan pembaca layar yang sedang berjalan, memfokuskan item yang dicentang ini akan memunculkan pengumuman seperti “tanda centang, item menu sedang, dicentang” .
Perilaku membuka menu dengan menuitemradio
yang dicentang sedikit berbeda. Alih-alih memfokuskan item pertama (diaktifkan) di menu, item yang dicentang akan difokuskan.
Apa manfaat dari perilaku ini? Pengguna (pengguna mana pun) diingatkan tentang opsi yang mereka pilih sebelumnya. Dalam menu dengan banyak opsi tambahan (misalnya, serangkaian tingkat zoom), orang yang mengoperasikan keyboard ditempatkan pada posisi optimal untuk melakukan penyesuaian.
Menggunakan Tombol Menu Dengan Pembaca Layar
Dalam video ini, saya akan menunjukkan kepada Anda bagaimana rasanya menggunakan tombol menu dengan pembaca layar Voiceover dan Chrome. Contoh menggunakan item dengan menuitemradio
, aria-checked
dan perilaku fokus yang dibahas. Pengalaman serupa dapat diharapkan di seluruh perangkat lunak pembaca layar populer.
Tombol Menu Inklusif Di Github
Kitty Giraudel dan saya telah bekerja sama dalam membuat komponen tombol menu dengan fitur API yang telah saya jelaskan, dan banyak lagi. Anda harus berterima kasih kepada Hugo atas banyak fitur ini, karena fitur ini didasarkan pada pekerjaan yang mereka lakukan pada dialog 11y — dialog modal yang dapat diakses. Ini tersedia di Github dan NPM.
npm i inclusive-menu-button --save
Selain itu, Kitty telah membuat versi React untuk kelezatan Anda.
Daftar periksa
- Jangan gunakan semantik menu ARIA dalam sistem menu navigasi.
- Di situs konten berat, jangan sembunyikan struktur di menu navigasi bertenaga dropdown bersarang.
- Gunakan
aria-expanded
untuk menunjukkan status buka/tutup dari menu navigasi yang diaktifkan tombol. - Pastikan menu navigasi tersebut berikutnya dalam urutan fokus setelah tombol yang membuka/menutupnya.
- Jangan pernah mengorbankan kegunaan dalam mengejar solusi bebas JavaScript. Ini kesombongan.