Verzoek om feedback van ontwikkelaars: focusgroep

Jacques Newman
Jacques Newman

Gepubliceerd: 5 maart 2026

Het HTML-attribuut focusgroup is een voorgestelde declaratieve manier om navigatie met de pijltjestoetsen toe te voegen aan samengestelde widgets zoals toolbars, tablijsten, menu's, lijstvakken, enz., zonder dat er JavaScript voor roving-tabindex nodig is. Eén attribuut vervangt honderden regels standaardcode. We willen graag uw feedback voordat dit wordt uitgebracht.

Probeer het uit en laat ons weten wat je ervan vindt.

Je kunt focusgroup vandaag nog uitproberen in Chrome, Edge en andere Chromium-browsers door het op een van de volgende twee manieren in te schakelen:

  1. Lokaal testen: Open in de browser de pagina about://flags en schakel de optie 'Experimental Web Platform features ' in. Of start de browser via de opdrachtregel met de parameter --enable-blink-features=Focusgroup .
  2. Origin-proefversie: Meld u aan voor de focusgroepproefversie van Origin om het op uw site te testen met echte gebruikers.

Verken vervolgens de interactieve demo's om elk patroon in actie te zien.

We hebben uw input nodig. Dien een focusgroepvraag in om ons te laten weten wat u ervan vindt.

Dit is een initiatief dat meerdere browsers omvat: het voorstel is afkomstig van Microsoft via de OpenUI Community Group, met sterke steun van Google. De API kan nog veranderen op basis van uw feedback. Laten we eens kijken naar het probleem dat de focusgroep oplost en hoe de API werkt.

Het probleem: handmatige tabindexering

Als je ooit een toolbar, tablist, menu of listbox hebt gemaakt, heb je een variant van deze code geschreven. De ARIA Authoring Practices Guide (APG) beveelt aan dat samengestelde widgets één tabstop weergeven en gebruikers met de pijltjes toetsen tussen items laten navigeren. Dit patroon staat bekend als de "roving tabindex". Veel UI-frameworks implementeren dit volledig opnieuw:

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

Vanaf hier moeten ontwikkelaars JavaScript gebruiken dat luistert naar de pijltjestoetsen om de focus te verplaatsen en het tabindex-attribuut voor alle elementen aan te passen. Dit is de vereenvoudigde versie. Een productie-implementatie moet ook het volgende afhandelen:

  • Schrijfmodus en RTL: Pas de richting van de pijltjestoetsen aan op basis van de schrijfrichting van de tekst.
  • Laatst gefocuste geheugen: Herstel de focus naar het eerder actieve item wanneer een gebruiker met de Tab-toets terugkeert naar een ander item.
  • Uitgeschakelde en verborgen items: Sla deze over tijdens het navigeren.
  • Dynamische items: Werk de roving-index bij wanneer items worden toegevoegd of verwijderd.

De meeste UI-bibliotheken, waaronder React , Angular CDK en Fluent UI, leveren elk hun eigen versie van deze logica. Dat is een hoop dubbel werk voor iets dat een platform-primitief zou kunnen zijn.

De oplossing: het focusgroup

Bij focusgroepen wordt dezelfde werkbalk als volgt:

<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>
De menubalk met de cursieve knop geselecteerd.

Probeer het live: Werkbalkpatroon > Basiswerkbalk . Dat is alles. Geen JavaScript nodig voor navigatie met de pijltjestoetsen. Geen handmatig beheer van de tabindex. Dit is wat de browser nu voor je doet:

  • Navigatie met pijltjestoetsen: Navigeer tussen items, waarbij de schrijfmodus en -richting worden gerespecteerd.
  • Eén tabblad: De browser voegt de deelnemende elementen automatisch samen tot één tabblad. Ontwikkelaars hoeven tabindex="-1" niet in te stellen voor niet-actieve elementen.
  • Laatst gefocuste geheugen: Wanneer een gebruiker de focusgroep verlaat en terugkeert, wordt de focus hersteld naar het item dat hij of zij had verlaten.
  • ARIA-semantiek: De browser kent de juiste rollen toe (zoals role="toolbar" ) op basis van het gekozen gedrag bij gebruik van generieke elementen.

Ontwikkelaars behouden alleen de logica die uniek is voor hun functies, zoals het wisselen van de ingedrukte status, het openen van menu's, het beheren van selecties of aangepaste commando's.

API-overzicht

Het attribuut focusgroup accepteert een door spaties gescheiden lijst van tokens. Het eerste token is altijd een gedragstoken dat het widgetpatroon declareert. Optionele modificatietokens volgen: focusgroup="<behavior> [inline|block] [wrap] [nomemory]" .

Gedragstokens

Het gedragstoken is vereist (tenzij none wordt gebruikt om je af te melden voor een focusgroep van een voorouder). Het declareert het samengestelde widgetpatroon, waardoor de juiste rollen kunnen worden afgeleid wanneer deze niet anderszins zijn gespecificeerd. De tokens volgen de patronen die worden beschreven in de Aria Authoring Practices-handleiding en staan ​​vermeld in de volgende tabel:

Gedrag APG-patroon Minimale containerrol (indien van toepassing) Minimale kinderrol
(indien van toepassing)
Standaardmodifiers
toolbar Werkbalk werkbalk (geen) inline
tablist APG-tabbladen tablijst tab inline wrap
radiogroup Radiogroep radiogroep radio (geen)
listbox Lijstvak lijstvak optie (geen)
menu Menu menu menu-item block wrap
menubar Menubalk menubalk menu-item inline wrap
none n.v.t. n.v.t. n.v.t. n.v.t.

Raadpleeg de toelichting voor alle details over hoe roltoewijzing werkt.

Asbeperking ( inline en block )

Als het gekozen gedrag geen standaardmodifiers heeft, werken alle vier de pijltjestoetsen om de focus te verplaatsen. Je kunt de navigatie beperken tot één logische as door de inline of block modifier te gebruiken:

  • inline : De focusgroep reageert alleen op de pijltjestoetsen op de inline-as, links en rechts in de meeste Engelstalige contexten ( horizontaal, van boven naar beneden) .
  • block : De focusgroep reageert alleen op de pijltjestoetsen op de blok-as, omhoog en omlaag in de meeste Engelstalige contexten (horizontaal, van boven naar beneden).

De asbeperking is afgestemd op de logische eigenschappen van CSS en past zich automatisch aan de schrijfmodus en -richting aan.

Omwikkelende navigatie

Standaard stopt de navigatie met de pijltjestoetsen aan de randen van de focusgroep. Voeg de wrap modifier toe om van het laatste item terug naar het eerste (en van het eerste terug naar het laatste) te springen. Als een gedrag standaard `wrap` heeft, gebruik dan de nowrap modifier om dit gedrag uit te schakelen.

Probeer het live: Tablijstpatroon > Horizontale tablijst met terugspringende focus . In dat voorbeeld springt de focus terug naar het tabblad Overzicht wanneer de focus op het tabblad FAQ staat en de gebruiker op de rechterpijltoets drukt.

Het focusgroupstart attribuut

Het attribuut focusgroupstart geeft aan welk element de focus krijgt wanneer er voor het eerst (of elke keer wanneer het geheugen is uitgeschakeld) naar een focusgroep wordt getabbed:

<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>
Een menubalk met de middelste knop geselecteerd.

Zowel Tab als Shift+Tab komen terecht op "Midden (Invoer)" omdat daar focusgroupstart is ingeschakeld en geheugen is uitgeschakeld met de nomemory modifier. Probeer het live: Werkbalkpatroon > Invoerpunt met focusgroupstart .

Geheugen uitschakelen (nomemory)

Standaard onthouden focusgroepen het laatst gefocuste item en herstellen dit bij het opnieuw openen met Tab. Voor patronen waarbij de focus altijd moet terugkeren naar een vast startpunt (zoals in de vorige demo), gebruikt u de modifier nomemory in het focusgroup-attribuut om dit uit te schakelen.

Deze modifier kan ook worden gecombineerd met programmatische verplaatsing van focusgroupstart , waardoor u volledige controle krijgt over het item dat wordt geselecteerd bij het betreden van de groep. Het geheugen wordt gewist wanneer het opgeslagen element niet meer beschikbaar is; bijvoorbeeld als het wordt verwijderd, verborgen, uitgeschakeld, inactief of uitgesloten van de focusgroep.

Afmelden ( focusgroup="none" )

Gebruik focusgroup="none" om een ​​element en de bijbehorende substructuur uit te sluiten van de pijlnavigatie van een focusgroep met een bovenliggend element. Het uitgesloten element en de bijbehorende substructuur blijven bereikbaar met de Tab-toets, maar de pijltjestoetsen slaan ze over.

<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>
Een menu waarin de knoppen Help en Sneltoets grijs zijn weergegeven.

Met de rechterpijltoets navigeert u naar Nieuw, vervolgens Openen, Opslaan, Sluiten en Afsluiten, waarbij de knoppen Help en Sneltoetsen volledig worden overgeslagen. Een gebruiker kan echter nog steeds met de Tab-toets naar de helpsectie gaan om deze knoppen te openen. Probeer het live: Aanvullende concepten > Segmenten uitsluiten met focusgroep="geen" .

Veelvoorkomende patronen

Tablijst

Een tabbesturingselement met navigatie tussen tabbladen via de pijltjestoetsen.

<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>
Het tabblad 'Overzicht' is geselecteerd.

Probeer het live: Tablist-patroon > Horizontale tablist met tekstterugloop .

Waarop te letten:

  • Het attribuut focusgroupstart bevindt zich op het geselecteerde tabblad, waardoor de focus daar altijd terechtkomt.
  • De nomemory modifier zorgt ervoor dat, zelfs als de gebruiker zich eerder op een ander tabblad had gericht, het opnieuw openen van het tabblad altijd terugkeert naar het geselecteerde tabblad.
  • De inline modifier beperkt de pijltjesnavigatie tot alleen de linker- en rechtertoetsen. Dit komt overeen met het verwachte gedrag zoals beschreven in het APG Tabs-patroon .
  • De wrap modifier stelt gebruikers in staat om de pijltjestoetsen continu te gebruiken bij het navigeren door alle tabbladen.
  • De ontwikkelaarscode, die hier voor de duidelijkheid is weggelaten, behandelt de daadwerkelijke selectie: het bijwerken van aria-selected , het wisselen van de paneelzichtbaarheid en het verplaatsen van het focusgroupstart attribuut bij een selectiewijziging.

Een eenvoudig verticaal menu met pijltjes omhoog en omlaag.

<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>
Een verticaal menu met het menu-item 'Openen' geselecteerd.

Probeer het live: Menu- en menubalkpatroon > Eenvoudig verticaal menu . Met de block navigeren alleen de pijltoetsen omhoog en omlaag door de items. De pijltoetsen naar links en rechts zijn vrij voor gedrag dat u zelf definieert (bijvoorbeeld het openen van submenu's). Voor een menubalk met geneste submenu's is elk niveau een onafhankelijke focusgroep. Probeer het live: Menu- en menubalkpatroon > Menubalk met pop-up submenu's

<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>
Een keuzemenu met het item 'kopiëren' geselecteerd.

Probeer het live: Menu- en menubalkpatroon > Menubalk met pop-up-submenu's . De menubalk gebruikt de inline modifier voor navigatie naar links en rechts, terwijl de submenu's de block modifier gebruiken voor navigatie naar boven en beneden. Geneste focusgroepen zijn volledig onafhankelijk, zodat ze elkaar niet beïnvloeden.

Radiogroep

Een op maat gemaakte radiogroep met navigatie via de pijltjestoetsen en volledige stylingcontrole.

<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>
Een groep keuzerondjes met de kleur blauw geselecteerd.

Probeer het live: Radiogroeppatroon > Vergelijking: Native versus Focusgroep .

Hoewel het focusgroup attribuut de navigatie met de pijltjestoetsen afhandelt, moet u zelf de selectiecode implementeren. In deze demo wordt de aangevinkte status beheerd door JavaScript-code (met behulp van het aria-checked -attribuut).

Kernconcepten

Deelname aan focusgroepitems

Alle opeenvolgend focusseerbare afstammelingen van het element met focusgroup die is ingesteld op een geldig gedrag, worden geacht deel te nemen aan die focusgroep. Dit betekent dat elementen met een negatieve tabindex niet worden meegenomen, maar van nature focusseerbare elementen zoals <button> wel, evenals elementen waar je een niet-negatieve tabindex hebt opgegeven.

Tab stop

Je hoeft geen tabindex waarden te beheren . Zelfs als meerdere afstammelingen van nature tabbaar zijn (bijvoorbeeld meerdere <button> -elementen), voegt focusgroup ze samen tot één tabstop. De browser bepaalt zelf welk element op een bepaald moment tabbaar is. Probeer het live: Toolbar Pattern > No tabindex Management Needed .

Laatst gefocuste herinnering

Standaard keert de focus terug naar het laatst gefocuste item wanneer een gebruiker op Tab drukt om een ​​focusgroep te verlaten en later weer terugkeert naar de focusgroep. Dit is cruciaal voor grote lijsten en toolbars, zodat gebruikers hun positie niet kwijtraken. Gebruik de nomemory -modifier om dit gedrag uit te schakelen als u wilt dat de focus altijd terugkeert naar het eerste element, of, als u focusgroupstart gebruikt, om het element te bepalen waarop de focus aanvankelijk stond.

Geneste focusgroepen

Elke focusgroepdeclaratie creëert een onafhankelijk bereik. Een geneste focusgroep schakelt automatisch de pijlnavigatie van de bovenliggende focusgroep uit. Gebruik de Tab-toets om tussen focusgroepen te schakelen en de pijltjestoetsen om binnen de huidige focusgroep te navigeren. Probeer het live: Aanvullende concepten > Geneste focusgroepen .

Shadow DOM-ondersteuning

Focusgroup is standaard van toepassing op alle DOM-grenzen. Een focusgroup die is gedeclareerd op een shadow host omvat focusbare elementen binnen de shadow tree van die host. Als u dit wilt uitschakelen, kunt u focusgroup="none" gebruiken in de shadow tree van uw component.

Kernpunten van conflicthantering

Sommige elementen binnen een focusgroep, zoals <input> , <textarea> en andere besturingselementen, gebruiken de pijltjestoetsen voor hun eigen doeleinden. Wanneer er een conflict is tussen de navigatietoetsen van de focusgroep en het gedrag van de pijltjestoetsen van een native element:

  • De pijltjestoetsen worden door het interactieve element gebruikt (bijvoorbeeld voor het verplaatsen van de tekstcursor) en FocusGroup heeft hier geen invloed op.
  • Tab of Shift+Tab biedt een standaard ontsnappingsmechanisme waarmee een gebruiker via tabnavigatie de focusgroep opnieuw kan betreden.

Deze ontsnappingsgedragingen zijn alleen van toepassing wanneer er daadwerkelijk een toetsconflict is; assen zonder conflict worden niet beïnvloed. U kunt ook preventDefault() aanroepen bij keydown -gebeurtenissen om het gedrag van de pijltoetsen van de focusgroep voor specifieke elementen te overschrijven. Dit betekent dat u invoervelden en tekstvelden in een focusgroep kunt opnemen zonder dat dit het gedrag van een van beide beïnvloedt.

Als u sleutelhandlers toevoegt aan uw eigen elementen die deelnemen aan een focusgroep, zorg er dan voor dat u een vergelijkbaar ontsnappingsmechanisme biedt, zodat gebruikers toegang hebben tot de rest van de groep.

Ontdekking van diepe afstammelingen

Focusgroepitems hoeven geen directe subitems te zijn van de focusgroepcontainer.

De browser beschouwt alle opeenvolgend focusseerbare afstammelingen (niet-negatieve tabindex ) als deelnemend aan de focusgroep, tenzij ze zich in een geneste focusgroep bevinden of zijn uitgesloten met 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>

Navigatie met de pijltjestoetsen werkt ook als de knoppen zich in <div> en <span> wrappers bevinden. Er is geen vereiste voor een platte lijst, dus wrapper-elementen voor styling zijn prima.

Probeer het live: Aanvullende concepten > Diepe afstammelingen .

Integratie met de reading-flow

Zowel sequentiële (Tab) als directionele (pijltoetsen) navigatie respecteren de CSS-eigenschap reading-flow indien aanwezig, en volgen de visuele leesvolgorde in plaats van de volgorde van de DOM-broncode.

Dit zorgt ervoor dat de navigatie met de pijltjestoetsen overeenkomt met de lay-out die gebruikers op het scherm zien.

<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 is scherp in beeld.

Hoewel de DOM-volgorde A, B, C is, is de visuele volgorde C, B, A omdat de lay-out gebruikmaakt van flex-direction: row-reverse . Omdat de code echter ook gebruikmaakt van reading-flow: flex-visual , is de leesvolgorde weer A, B, C, en focusgroup komt overeen met deze volgorde.

Door op Tab te drukken, wordt eerst C geselecteerd, en door naar rechts te drukken, wordt vervolgens B en daarna A geselecteerd. Probeer het live: Aanvullende concepten > CSS-leesstroomintegratie .

Toegankelijkheid

ARIA-rolinferentie

In een focusgroep gebruikt de browser het gedragstoken om een ​​minimale rol af te leiden voor zowel de container als de deelnemende elementen. Dit betekent dat wanneer het focusgroup attribuut is ingesteld op een element met een generieke rol, de juiste rol, gebaseerd op het gekozen gedrag, wordt toegepast. De deelnemende elementen van het element die een generieke rol hebben, of knoppen die geen door u opgegeven rol hebben, krijgen hun rollen dienovereenkomstig afgeleid. Bijvoorbeeld de volgende HTML:

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

Er wordt de volgende toegankelijkheidsstructuur aangemaakt, ook al zijn er geen rollen gedefinieerd voor de knoppen:

+   tablist
  |
  +   tab
  |
  +   tab
  |
  +   tab

Je kunt het gedrag altijd rechtstreeks beïnvloeden door de rol in te stellen.

Toegankelijkheidsaspecten

Zorg ervoor dat u het gedrag respecteert dat u hebt gekozen bij het samenstellen van een focusgroep.

Het gebruik van focusgroepen moet zo goed mogelijk aansluiten bij het gedrag dat u hebt gespecificeerd. Dit is belangrijk om ervoor te zorgen dat gebruikers die afhankelijk zijn van toegankelijkheidstools de inhoud kunnen navigeren en aangepaste bedieningselementen kunnen gebruiken.

Hoewel rolafleiding goede standaardwaarden biedt, is het bij het gebruik van elementen met niet-generieke rollen belangrijk om ervoor te zorgen dat ze de juiste rol hebben voor de functionaliteit die ze bieden.

Houd er bij het gebruik van Focusgroup rekening mee dat gebruikers mogelijk met de pijltjes toetsen moeten kunnen scrollen om de inhoud te bekijken. Er moet altijd een manier zijn voor toetsenbordgebruikers om de inhoud van uw pagina te kunnen lezen en openen.

Kenmerkdetectie

Om focusgroup vandaag al te gebruiken, voordat het volledig door alle browsers wordt ondersteund, kunt u de ondersteuning voor focusgroup in JavaScript detecteren:

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

Conclusie

Het focusgroup doorloopt momenteel de standaardisatieprocedures en we werken actief aan het prototype in Chromium en de verfijning van de API.

Probeer het uit en dien een focusgroepvraag in bij de Open-UI GitHub issue tracker. We zijn met name geïnteresseerd in uw mening over de volgende punten:

  • Voelt de API-interface goed aan voor de patronen die je ontwikkelt?
  • Zijn er patronen of scenario's die we over het hoofd zien?
  • Zijn er elementen waarop het focusgroup-attribuut niet zou moeten worden toegepast?
  • Hoe past het toegankelijkheidsaspect in uw specifieke gebruikssituaties?

Bedankt voor je bijdrage aan het verbeteren van de toetsenbordnavigatie op het web!

Leer meer

Met dank aan Mason Freed, Sara Higley, Scott O'Hara en de rest van de Open-UI-gemeenschap voor hun hulp bij het terugbrengen van focusgroepen.