Permintaan masukan developer: focusgroup

Jacques Newman
Jacques Newman

Dipublikasikan: 5 Maret 2026

Atribut HTML focusgroup adalah cara deklaratif yang diusulkan untuk menambahkan navigasi tombol panah keyboard ke widget komposit seperti toolbar, tablist, menu, listbox, dll. tanpa menulis JavaScript roving-tabindex. Satu atribut menggantikan ratusan baris boilerplate. Kami ingin mendengar masukan Anda sebelum fitur ini diluncurkan.

Cobalah dan sampaikan masukan Anda kepada kami

Anda dapat mencoba focusgroup hari ini di Chrome, Edge, dan browser berbasis Chromium lainnya dengan mengaktifkannya melalui salah satu dari dua cara berikut:

  1. Pengujian lokal: Di browser, buka halaman about://flags dan aktifkan tanda Fitur Platform Web Eksperimental. Atau, luncurkan browser dari command line dengan menggunakan parameter command line --enable-blink-features=Focusgroup.
  2. Uji coba origin: Daftar untuk uji coba origin focusgroup untuk mengujinya di situs Anda dengan pengguna sungguhan.

Kemudian, pelajari demo interaktif untuk melihat cara kerja setiap pola.

Kami memerlukan masukan Anda. Ajukan masalah grup fokus untuk menyampaikan pendapat Anda.

Ini adalah upaya lintas browser: proposal ini berasal dari Microsoft melalui Grup Komunitas OpenUI dengan dukungan kuat dari Google. Struktur API dapat berubah berdasarkan masukan Anda. Mari kita pelajari masalah yang diselesaikan oleh grup fokus dan cara kerja API.

Masalah: indeks tab jelajah manual

Jika Anda pernah membuat toolbar, tablist, menu, atau listbox, Anda telah menulis beberapa versi kode ini. Panduan Praktik Penulisan ARIA (APG) merekomendasikan agar widget komposit menampilkan satu Tab stop dan memungkinkan pengguna berpindah antar-item dengan tombol panah. Pola ini dikenal sebagai "tabindex bergerak". Banyak framework UI yang mengimplementasikan ulang hal ini dari awal:

<div role="toolbar" aria-label="Text formatting" id="toolbar">
  <button type="button" tabindex="0">Bold</button>
  <button type="button" tabindex="-1">Italic</button>
  <button type="button" tabindex="-1">Underline</button>
  <button type="button" tabindex="-1">Strikethrough</button>
</div>

Dari sini, developer perlu menggunakan JavaScript yang memproses tombol panah untuk memindahkan fokus, dan menyesuaikan atribut tabindex untuk semua elemen. Ini adalah versi sederhana. Implementasi produksi juga perlu menangani:

  • Mode penulisan dan RTL: Menyesuaikan arah tombol panah berdasarkan arah konten.
  • Memori fokus terakhir: Memulihkan fokus ke item yang sebelumnya aktif saat pengguna kembali ke Tab.
  • Item yang dinonaktifkan dan disembunyikan: Dilewati selama navigasi.
  • Item dinamis: Perbarui indeks roaming saat item ditambahkan atau dihapus.

Sebagian besar library UI, termasuk React, Angular CDK, dan Fluent UI, masing-masing mengirimkan versi logika ini sendiri. Ada banyak upaya duplikat untuk mendapatkan sesuatu yang bisa menjadi primitif platform.

Solusi: atribut focusgroup

Dengan focusgroup, toolbar yang sama menjadi:

<div focusgroup="toolbar" aria-label="Text formatting">
  <button type="button">Bold</button>
  <button type="button">Italic</button>
  <button type="button">Underline</button>
  <button type="button">Strikethrough</button>
</div>
Menu dengan tombol miring difokuskan.

Coba secara langsung: Pola Toolbar > Toolbar Dasar. Selesai. Tidak ada JavaScript untuk navigasi tombol panah. Tidak ada pengelolaan tabindex manual. Berikut yang kini ditangani browser untuk Anda:

  • Navigasi tombol panah: Beralih antar-item, sambil memperhatikan mode dan arah penulisan.
  • Satu Perhentian tab: Browser akan menciutkan item yang berpartisipasi menjadi satu Perhentian tab secara otomatis. Developer tidak perlu menetapkan tabindex="-1" pada item yang tidak aktif.
  • Memori fokus terakhir: Saat pengguna keluar dari focusgroup dan kembali, fokus dipulihkan ke item yang ditinggalkannya.
  • Semantik ARIA: Browser menyediakan peran yang sesuai (seperti role="toolbar") berdasarkan perilaku yang dipilih saat elemen generik digunakan.

Developer hanya menyimpan logika yang unik untuk fitur mereka seperti mengubah status ditekan, membuka menu, mengelola pilihan, atau perintah kustom apa pun.

Ringkasan API

Atribut focusgroup mengambil daftar token yang dipisahkan spasi. Token pertama selalu berupa token perilaku yang mendeklarasikan pola widget. Token pengubah opsional adalah sebagai berikut: focusgroup="<behavior> [inline|block] [wrap] [nomemory]".

Token perilaku

Token perilaku diperlukan (kecuali jika menggunakan none untuk memilih tidak ikut grup fokus induk). Pola widget komposit dideklarasikan, sehingga memastikan bahwa peran yang tepat dapat disimpulkan jika tidak ditentukan. Token mengikuti pola yang dijelaskan dalam panduan Praktik Penulisan Aria dan tercantum dalam tabel berikut:

Perilaku Pola APG Peran minimum penampung (jika diterapkan) Peran turunan minimum
(jika diterapkan)
Pengubah default
toolbar Toolbar toolbar (tidak ada) inline
tablist Tab APG tablist tab inline wrap
radiogroup Grup Radio radiogroup radio (tidak ada)
listbox Kotak daftar listbox option (tidak ada)
menu Menu menu menuitem block wrap
menubar Menubar menubar menuitem inline wrap
none t/a t/a t/a t/a

Lihat penjelasan untuk mengetahui detail lengkap tentang cara kerja pemetaan peran.

Pembatasan sumbu (inline dan block)

Jika perilaku yang dipilih tidak memiliki pengubah default, keempat tombol panah akan berfungsi untuk memindahkan fokus. Anda dapat membatasi navigasi ke satu sumbu logis dengan menggunakan pengubah inline atau block:

  • inline: focusgroup hanya merespons tombol panah pada sumbu inline, kiri dan kanan dalam sebagian besar konteks bahasa Inggris (horizontal, atas ke bawah).
  • block: focusgroup hanya merespons tombol panah pada sumbu blok, atas dan bawah dalam sebagian besar konteks bahasa Inggris (horizontal, atas ke bawah).

Pembatasan sumbu selaras dengan properti logis CSS dan otomatis beradaptasi dengan mode dan arah penulisan.

Navigasi wrap-around

Secara default, navigasi tombol panah berhenti di tepi focusgroup. Tambahkan pengubah wrap untuk melakukan loop dari item terakhir kembali ke item pertama (dan dari item pertama kembali ke item terakhir). Jika perilaku memiliki pembungkusan secara default, gunakan pengubah nowrap untuk menonaktifkan perilaku ini.

Coba langsung: Tablist Pattern > Horizontal Tablist with Wrapping. Dalam contoh tersebut, saat fokus berada di tab FAQ dan pengguna menekan tombol panah Kanan, fokus akan kembali ke tab Ringkasan.

Atribut focusgroupstart

Atribut focusgroupstart menandai elemen mana yang menerima fokus saat melakukan Tab ke dalam focusgroup untuk pertama kalinya (atau setiap kali saat memori dinonaktifkan):

<div focusgroup="toolbar nomemory" aria-label="Entry point demo">
  <button type="button">First</button>
  <button type="button" focusgroupstart>Middle (Entry)</button>
  <button type="button">Last</button>
</div>
Menu bar dengan tombol tengah dalam fokus.

Tab dan Shift+Tab mendarat di "Middle (Entry)" karena memiliki focusgroupstart dan memori dinonaktifkan dengan pengubah nomemory. Coba secara langsung: Pola Toolbar > Titik Entri dengan focusgroupstart.

Nonaktifkan memori (nomemory)

Secara default, focusgroup mengingat item yang terakhir difokuskan dan memulihkannya saat masuk kembali dengan Tab. Untuk pola yang fokusnya harus selalu kembali ke titik entri tetap (seperti dalam demo sebelumnya), gunakan pengubah nomemory di atribut focusgroup untuk menonaktifkannya.

Pengubah ini juga dapat digabungkan dengan gerakan terprogram focusgroupstart untuk memberi Anda kontrol penuh atas item yang difokuskan saat memasuki grup. Memori akan dibersihkan saat elemen yang diingat menjadi tidak tersedia; misalnya, jika elemen tersebut dihapus, disembunyikan, dinonaktifkan, tidak aktif, atau dikecualikan dari focusgroup.

Memilih tidak ikut (focusgroup="none")

Gunakan focusgroup="none" untuk mengecualikan elemen dan subtre-nya dari navigasi panah focusgroup induk. Elemen yang tidak diikutsertakan dan subpohonnya tetap dapat dijangkau menggunakan Tab, tetapi tombol panah akan melewatinya:

<div focusgroup="toolbar" aria-label="Segmented toolbar">
  <button type="button">New</button>
  <button type="button">Open</button>
  <button type="button">Save</button>
  <span focusgroup="none">
    <button type="button">Help</button>
    <button type="button">Shortcuts</button>
  </span>
  <button type="button">Close</button>
  <button type="button">Exit</button>
</div>
Menu dengan tombol Bantuan dan Pintasan berwarna abu-abu.

Menggunakan tombol panah kanan akan membuka opsi Baru, lalu Buka, Simpan, Tutup, dan Keluar, serta melewati tombol Bantuan dan Pintasan. Namun, pengguna tetap dapat menekan Tab untuk membuka bagian bantuan guna mengakses tombol ini. Coba secara langsung: Konsep Tambahan > Segmen yang Tidak Bersedia dengan focusgroup="none".

Pola umum

Tablist

Kontrol tab dengan navigasi tombol panah di antara tab.

<div focusgroup="tablist nomemory" aria-label="Sections">
  <button type="button" aria-selected="true" aria-controls="panel-overview" id="tab-overview" focusgroupstart>Overview</button>
  <button type="button" aria-selected="false" aria-controls="panel-features" id="tab-features">Features</button>
  <button type="button" aria-selected="false" aria-controls="panel-pricing" id="tab-pricing">Pricing</button>
  <button type="button" aria-selected="false" aria-controls="panel-faq" id="tab-faq">FAQ</button>
</div>
<div role="tabpanel" id="panel-overview" aria-labelledby="tab-overview" tabindex="0">...</div>
<div role="tabpanel" id="panel-features" aria-labelledby="tab-features" tabindex="0">...</div>
<div role="tabpanel" id="panel-pricing" aria-labelledby="tab-pricing" tabindex="0">...</div>
<div role="tabpanel" id="panel-faq" aria-labelledby="tab-faq" tabindex="0">...</div>
Tab ringkasan dalam fokus.

Coba langsung: Tablist Pattern > Horizontal Tablist with Wrapping.

Hal yang perlu diperhatikan:

  • Atribut focusgroupstart ada di tab selected, sehingga fokus selalu masuk ke sana.
  • Pengubah nomemory memastikan bahwa meskipun pengguna sebelumnya telah memfokuskan pada tab yang berbeda, entri ulang selalu menuju ke tab yang dipilih.
  • Pengubah inline membatasi navigasi panah hanya ke tombol kiri dan kanan. Hal ini sesuai dengan perilaku yang diharapkan yang diuraikan oleh pola Tab APG.
  • Pengubah wrap memungkinkan pengguna menggunakan tombol panah secara terus-menerus di semua tab.
  • Kode developer, yang dihilangkan agar lebih ringkas, menangani pilihan sebenarnya: memperbarui aria-selected, mengganti visibilitas panel, dan memindahkan atribut focusgroupstart pada perubahan pilihan.

Menu vertikal sederhana dengan navigasi panah atas dan bawah.

<div focusgroup="menu" aria-label="File actions" class="menu-vertical">
    <button type="button" class="menu-item">New</button>
    <button type="button" class="menu-item">Open…</button>
    <button type="button" class="menu-item">Save</button>
    <button type="button" class="menu-item">Exit</button>
</div>
Menu vertikal dengan item menu Buka dalam fokus.

Coba langsung: Pola Menu dan Menu Bar > Menu Vertikal Sederhana. Dengan pengubah block, hanya tombol panah atas dan bawah yang dapat menavigasi item. Tombol panah kiri dan kanan bebas untuk perilaku yang Anda tentukan (misalnya, membuka submenu). Untuk menubar dengan submenu bertingkat, setiap level adalah grup fokus independen. Coba langsung: Pola Menu dan Panel menu > Panel menu dengan Submenu Popover

<ul role="menubar" focusgroup="menubar"
     aria-label="Application Menu" class="menubar">
    <li role="none">
        <button role="menuitem" type="button" class="menubar-item"
             aria-haspopup="menu" aria-expanded="false"
             popovertarget="filemenu">File</button>
        <ul role="menu" focusgroup="menu"
             id="filemenu" popover aria-label="File submenu" class="submenu">
            <li role="none"><button type="button" class="submenu-item"
                 autofocus>New</button></li>
            <li role="none"><button type="button" class="submenu-item">Open</button></li>
            <li role="none"><button type="button" class="submenu-item">Save</button></li>
        </ul>
    </li>
    <!-- More menu items... -->
</ul>
Menu dropdown dengan item salin dalam fokus.

Coba langsung: Pola Menu dan Menu Bar > Menu Bar dengan Submenu Popover. Meskipun menubar menggunakan pengubah inline untuk navigasi kiri dan kanan, submenu menggunakan pengubah block untuk navigasi atas dan bawah. Grup fokus bertingkat benar-benar independen sehingga tidak saling mengganggu.

Radiogroup

Grup tombol pilihan kustom dengan navigasi tombol panah dan kontrol gaya penuh.

<div focusgroup="radiogroup" aria-label="Favorite color">
  <span aria-checked="false" tabindex="0">Red</span>
  <span aria-checked="false" tabindex="0">Green</span>
  <span aria-checked="true" tabindex="0" focusgroupstart >Blue</span>
  <span aria-checked="false" tabindex="0">Purple</span>
</div>
Grup tombol pilihan dengan Blue dalam fokus.

Coba langsung: Pola Grup Radio > Perbandingan: Native versus Focusgroup.

Meskipun atribut focusgroup menangani navigasi tombol panah, Anda harus menerapkan kode pemilihan. Dalam demo ini, kode JavaScript mengelola status yang dicentang (dengan menggunakan atribut aria-checked).

Konsep utama

Partisipasi item grup fokus

Semua turunan yang dapat difokuskan secara berurutan dari elemen dengan focusgroup yang ditetapkan ke perilaku yang valid dianggap berpartisipasi dalam grup fokus tersebut. Artinya, elemen dengan tabindex negatif tidak dipertimbangkan, tetapi elemen yang dapat difokuskan secara native seperti <button>, serta elemen tempat Anda menentukan tabindex non-negatif, akan dipertimbangkan.

Tab stop

Anda tidak perlu mengelola nilai tabindex. Meskipun beberapa turunan dapat di-tab secara alami (misalnya, beberapa elemen <button>), focusgroup akan menciutkannya menjadi satu Tab stop. Browser menangani item mana yang dapat difokuskan dengan tombol tab pada waktu tertentu. Coba secara live: Pola Toolbar > Tidak Perlu Pengelolaan tabindex.

Memori yang terakhir difokuskan

Secara default, saat pengguna menekan Tab untuk keluar dari focusgroup dan kemudian menekan Tab kembali, fokus akan kembali ke item yang terakhir difokuskan. Hal ini sangat penting untuk daftar dan toolbar yang besar agar pengguna tidak kehilangan bagian yang sedang mereka lihat. Gunakan pengubah nomemory untuk menonaktifkan perilaku ini jika Anda ingin fokus selalu dipulihkan ke elemen pertama, atau jika Anda menggunakan focusgroupstart, untuk mengontrol elemen yang difokuskan pada awalnya.

Grup fokus bertingkat

Setiap deklarasi focusgroup membuat cakupan independen. Grup fokus bertingkat secara otomatis menonaktifkan navigasi panah induknya. Gunakan Tab untuk berpindah-pindah antar-grup fokus, dan tombol panah untuk menjelajahi dalam grup fokus saat ini. Coba secara langsung: Konsep Tambahan > Grup Fokus Bertingkat.

Dukungan Shadow DOM

Focusgroup diterapkan di seluruh batas DOM bayangan secara default. Grup fokus yang dideklarasikan di host bayangan mencakup elemen yang dapat difokuskan di dalam hierarki bayangan host tersebut. Jika ingin memilih tidak ikut, Anda dapat menggunakan focusgroup="none" di dalam shadow tree komponen.

Penanganan konflik kunci

Beberapa elemen di dalam focusgroup, seperti <input>, <textarea>, dan kontrol lainnya menggunakan tombol panah untuk tujuan mereka sendiri. Jika ada konflik antara tombol navigasi grup fokus dan perilaku tombol panah elemen native:

  • Tombol panah digunakan oleh elemen interaktif (misalnya, untuk pergerakan kursor teks), dan focusgroup tidak mengganggu.
  • Tab atau Shift+Tab menyediakan mekanisme keluar default, yang memungkinkan pengguna menggunakan navigasi Tab untuk "masuk kembali" ke focusgroup.

Perilaku pelepasan ini hanya berlaku jika ada konflik tombol aktual; sumbu yang tidak berkonflik tidak terpengaruh. Anda juga dapat memanggil preventDefault() pada peristiwa keydown untuk mengganti perilaku tombol panah focusgroup untuk elemen tertentu. Artinya, Anda dapat menyertakan input dan textarea di dalam focusgroup tanpa merusak perilaku tersebut.

Jika Anda menambahkan pengendali tombol ke elemen Anda sendiri yang berpartisipasi dalam grup fokus, berhati-hatilah untuk menyediakan mekanisme keluar yang serupa sehingga pengguna dapat mengakses grup lainnya.

Penemuan turunan mendalam

Item focusgroup tidak harus berupa turunan langsung dari penampung focusgroup.

Browser menganggap semua turunan yang dapat difokuskan secara berurutan (non-negatif tabindex) berpartisipasi dalam focusgroup, kecuali jika berada di dalam focusgroup bertingkat atau tidak diikutsertakan dengan focusgroup="none".

<div focusgroup="toolbar" aria-label="Nested wrappers">
  <div>
    <span>
      <button type="button">Alpha</button>
    </span>
    <span>
      <button type="button">Beta</button>
    </span>
    <span>
      <button type="button">Gamma</button>
    </span>
  </div>
</div>

Navigasi tombol panah berfungsi meskipun tombol disarangkan di dalam wrapper <div> dan <span>. Tidak ada persyaratan daftar datar, sehingga elemen wrapper untuk penataan gaya tidak masalah.

Coba secara langsung: Konsep Tambahan > Turunan Dalam.

Integrasi dengan properti reading-flow

Navigasi berurutan (Tab) dan terarah (tombol panah) mematuhi properti CSS reading-flow jika ada, mengikuti urutan baca visual, bukan urutan sumber DOM.

Hal ini memastikan bahwa navigasi tombol panah cocok dengan tata letak yang dilihat pengguna di layar.

<div focusgroup="toolbar" aria-label="Visual order"
     style="display: flex; flex-direction: row-reverse; reading-flow: flex-visual;">
  <button type="button">A (DOM first)</button>
  <button type="button">B (DOM second)</button>
  <button type="button">C (DOM third)</button>
</div>
Item A dalam fokus.

Meskipun urutan DOM adalah A, B, C, urutan visualnya adalah C, B, A karena tata letaknya menggunakan flex-direction: row-reverse. Namun, karena kode juga menggunakan reading-flow: flex-visual, urutan pembacaan kembali menjadi A, B, C, dan grup fokus cocok dengan urutan ini.

Menekan Tab akan memfokuskan C terlebih dahulu, lalu menekan kanan akan memfokuskan B, lalu A. Coba langsung: Additional Concepts > CSS reading-flow Integration.

Aksesibilitas

Inferensi Peran ARIA

Dalam grup fokus, token perilaku digunakan oleh browser untuk menyimpulkan peran minimum bagi penampung dan item yang berpartisipasi di dalamnya. Artinya, saat atribut focusgroup ditetapkan pada elemen yang memiliki peran generik, peran yang benar, berdasarkan perilaku yang dipilih, akan diterapkan. Item yang berpartisipasi dalam elemen yang memiliki peran umum, atau tombol yang tidak memiliki peran yang Anda tentukan, akan memiliki peran yang disimpulkan secara sesuai. Misalnya, HTML berikut:

<div focusgroup="tablist">
  <button>Tab 1</button>
  <button>Tab 2</button>
  <button>Tab 3</button>
</div>

Membuat hierarki aksesibilitas berikut, meskipun tidak ada peran yang ditentukan pada tombol:

+   tablist
  |
  +   tab
  |
  +   tab
  |
  +   tab

Anda selalu dapat mengontrol perilaku dengan menetapkan peran secara langsung.

Pertimbangan aksesibilitas

Pastikan untuk menghormati perilaku yang Anda pilih saat membuat grup fokus.

Penggunaan Focusgroup harus disesuaikan sedekat mungkin dengan perilaku yang Anda tentukan. Hal ini penting untuk memastikan bahwa pengguna yang mengandalkan alat aksesibilitas dapat menjelajahi konten dan menggunakan kontrol kustom.

Meskipun inferensi peran memberikan default yang baik, saat menggunakan elemen dengan peran non-generik, berhati-hatilah untuk memastikan bahwa elemen tersebut memiliki setelan peran yang tepat untuk fungsi yang mereka berikan.

Saat menggunakan focusgroup, ingatlah bahwa pengguna mungkin perlu dapat men-scroll dengan tombol panah untuk melihat konten Anda. Pengguna keyboard harus selalu dapat membaca dan mengakses konten di halaman Anda.

Deteksi fitur

Untuk mulai menggunakan focusgroup hari ini, sebelum didukung sepenuhnya di seluruh browser, Anda dapat mendeteksi dukungan focusgroup di JavaScript:

if ('focusgroup' in HTMLElement.prototype) {
  // focusgroup is supported.
} else {
  // fall back to manual roving tabindex.
}

Kesimpulan

Atribut focusgroup sedang diproses oleh badan standar, dan kami secara aktif membangun prototipe di Chromium dan menyempurnakan API.

Coba dan ajukan masalah grup fokus di issue tracker GitHub Open-UI. Kami sangat tertarik dengan pendapat Anda tentang hal-hal berikut:

  • Apakah platform API terasa tepat untuk pola yang Anda buat?
  • Apakah ada pola atau skenario yang terlewat?
  • Apakah ada elemen yang tidak boleh memiliki atribut focusgroup?
  • Bagaimana cara kerja kisah aksesibilitas untuk kasus penggunaan Anda?

Terima kasih telah membantu meningkatkan kualitas navigasi keyboard di web.

Pelajari lebih lanjut

Terima kasih kepada Mason Freed, Sara Higley, Scott O'Hara, dan komunitas Open-UI lainnya atas bantuan mereka dalam menghadirkan kembali focusgroup.