Rumah > Artikel > hujung hadapan web > Membina Bar Menu Navigasi Boleh Diakses dengan Cangkuk React
Diterbitkan secara tidak sengaja! Sila kembali kemudian untuk mendapatkan maklumat lanjut!
Mencipta aplikasi web yang boleh diakses bukan sekadar amalan yang baik — ia adalah satu keperluan sekarang. Baru-baru ini, saya berpeluang membina bar menu navigasi dengan fokus pada a11y. Semasa saya meneliti, saya menyedari bagaimana kebanyakan bar menu di luar sana tidak mematuhi corak ARIA. Sebagai contoh, daripada menyemak item menu, adakah anda tahu bahawa bar menu harus dilayari dengan kekunci anak panah dan mengurus fokusnya sendiri?
Walaupun saya telah menemui beberapa tutorial, saya akhirnya tidak mengikutinya sepenuhnya. Saya menulis ini kerana saya fikir perkara yang saya bina itu patut dikongsi — jika anda juga mempunyai pertalian dengan komponen kecil dan cangkuk tersuai.
Walaupun saya akan menstrukturkan blog ini dengan beberapa langkah pembangunan, matlamat saya bukanlah untuk menulis panduan langkah demi langkah. Saya percaya anda mengetahui asas React, dan cara cangkuk tersuai berfungsi.
Saya hanya berkongsi butiran pelaksanaan utama sekarang, tetapi saya bercadang untuk mengemas kini artikel ini dengan contoh kotak pasir kod pada masa hadapan apabila saya mempunyai lebih banyak masa.
Untuk blog ini, kami sedang membina bar menu navigasi, seperti yang anda lihat di bahagian atas atau di sisi banyak aplikasi web. Dalam bar menu ini, sesetengah item menu mungkin mempunyai sub menu, yang akan dibuka/tutup pada tetikus masuk/keluar.
Pertama sekali, HTML semantik dan peranan yang sesuai serta atribut ARIA adalah penting untuk kebolehaksesan. Untuk corak menubar, anda boleh membaca lebih lanjut daripada dokumen rasmi di sini.
Berikut ialah contoh untuk penanda HTML yang sesuai:
<nav aria-label="Accessible Menubar"> <menu role="menubar"> <li role="none"> <a role="menuitem" href="/">Home</a> </li> <li role="none"> <a role="menuitem" href="/about">About</a> </li> <li role="none"> <button role="menuitem" aria-haspopup="true" aria-expanded="false" > Expand Me! </button> <menu role="menu"> <li role="none"> <a role="menuitem" href="/sub-item-1">Sub Menu Item 1</a> </li> <li role="none"> <a role="menuitem" href="/sub-item-2">Sub Menu Item 2</a> </li> </menu> </li> </menu> </nav>
Perhatikan kami menggunakan teg butang untuk HTML semantik. Butang juga harus mempunyai aria-haspopup untuk memberi amaran kepada pembaca skrin. Akhir sekali, atribut dikembangkan aria yang sesuai hendaklah ditetapkan bergantung pada keadaan menu.
Mari kita lihat komponen yang kita perlukan. Jelas sekali, kami memerlukan komponen menu keseluruhan, serta komponen item menu.
Sesetengah item menu mempunyai submenu manakala ada yang tidak. Item menu dengan sub menu perlu mengurus keadaannya untuk sub menu buka/tutup pada acara tuding dan papan kekunci. Jadi ia perlu menjadi komponennya sendiri.
Sub menu juga perlu menjadi komponennya sendiri. Walaupun sub menu juga hanyalah bekas untuk item menu, mereka tidak mengurus keadaan mereka atau mengendalikan acara papan kekunci. Ini membezakannya daripada menu nav peringkat atas.
Saya akhirnya menulis komponen ini:
Dalam perkataan yang sangat jelas, "pengurusan fokus" hanya bermaksud komponen perlu mengetahui kanak-kanak mana yang mempunyai fokus. Jadi apabila fokus pengguna keluar dan kembali, kanak-kanak yang fokus sebelum ini akan difokuskan semula.
Teknik biasa untuk pengurusan fokus ialah "Indeks Tab Berkeliaran", di mana elemen fokus dalam kumpulan mempunyai indeks tab 0 dan elemen lain mempunyai indeks tab -1. Dengan cara ini, apabila pengguna kembali ke kumpulan fokus, elemen dengan indeks tab 0 akan mempunyai fokus secara automatik.
Pelaksanaan pertama untuk NavMenu boleh kelihatan seperti ini:
export function NavMenu ({ menuItems }) { // state for the currently focused index const [focusedIndex, setFocusedIndex] = useState(0); // functions to update focused index const goToStart = () => setCurrentIndex(0); const goToEnd = () => setCurrentIndex(menuItems.length - 1); const goToPrev = () => { const index = currentIndex === 0 ? menuItems.length - 1 : currentIndex - 1; setCurrentIndex(index); }; const goToNext = () => { const index = currentIndex === menuItems.length - 1 ? 0 : currentIndex + 1; setCurrentIndex(index); }; // key down handler according to aria specification const handleKeyDown = (e) => { e.stopPropagation(); switch (e.code) { case "ArrowLeft": case "ArrowUp": e.preventDefault(); goToPrev(); break; case "ArrowRight": case "ArrowDown": e.preventDefault(); goToNext(); break; case "End": e.preventDefault(); goToEnd(); break; case "Home": e.preventDefault(); goToStart(); break; default: break; } } return ( <nav> <menu role="menubar" onKeyDown={handleKeyDown}> {menuItems.map((item, index) => <MenuItem key={item.label} item={item} index={index} focusedIndex={focusedIndex} setFocusedIndex={setFocusedIndex} /> )} </menu> </nav> ); }
e.preventDefault() ada untuk menghalang perkara seperti ArrowDown menatal halaman.
Inilah komponen MenuItem. Mari abaikan item dengan sub menu hanya untuk seketika. Kami menggunakan useEffect, usePrevious dan element.focus() untuk memfokus pada elemen apabila focusedIndex berubah:
export function MenuItem ({ item, index, focusedIndex, setFocusedIndex }) { const linkRef = useRef(null); const prevFocusedIndex = usePrevious(focusedIndex); const isFocused = index === focusedIndex; useEffect(() => { if (linkRef.current && prevFocusedIndex !== currentIndex && isFocused) { linkRef.current.focus() } }, [isFocused, prevFocusedIndex, focusedIndex]); const handleFocus = () => { if (focusedIndex !== index) { setFocusedIndex(index); } }; return ( <li role="none"> <a ref={linkRef} role="menuitem" tabIndex={isFocused ? 0 : -1} onFocus={handleFocus} > {item.label} </a> </li> ); }
Perhatikan bahawa teg yang sepatutnya mempunyai ref (butang untuk item menu dengan submenu), jadi apabila ia difokuskan, gelagat papan kekunci lalai akan bermula seperti yang dijangkakan, seperti navigasi pada Enter. Apatah lagi, indeks tab ditetapkan dengan betul bergantung pada elemen yang difokuskan.
Kami menambah pengendali acara untuk acara fokus sekiranya acara fokus bukan daripada acara kunci/tikus. Berikut ialah petikan daripada dokumen web:
Jangan menganggap bahawa semua perubahan fokus akan datang melalui acara kekunci dan tetikus: teknologi bantuan seperti pembaca skrin boleh menetapkan fokus kepada mana-mana elemen boleh fokus.
Jika anda mengikuti useEffect yang diterangkan di atas, anda akan mendapati bahawa elemen pertama akan mempunyai fokus walaupun pengguna belum menggunakan papan kekunci untuk menavigasi. Untuk membetulkannya, kami boleh menyemak elemen aktif dan hanya memanggil fokus() apabila pengguna telah memulakan beberapa acara papan kekunci, yang mengalihkan fokus daripada badan.
useEffect(() => { if (linkRef.current && document.activeElement !== document.body // only call focus when user uses keyboard navigation && prevFocusedIndex !== focusedIndex && isCurrent) { linkRef.current.focus(); } }, [isCurrent, focusedIndex, prevFocusedIndex]);
So far, we have functional NavMenu and MenuItemLink components. Let's move on to menu item with sub menus.
As I was quickly building it out, I realized that this menu item will share the majority of the logic
Atas ialah kandungan terperinci Membina Bar Menu Navigasi Boleh Diakses dengan Cangkuk React. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!