# Attributes and options > Configure keyboard navigation with data-keyrove-* attributes, JavaScript options or both, including how options fall back and where the two differ. Configure a group with `data-keyrove-*` attributes, JavaScript options, or both. Options override attributes one setting at a time. Some options have different names or scope; see the [differences below](#configuration-differences). Here is one group both ways. It is the same list, navigated identically: ```html title="Attributes"
``` ```ts title="Options" // menu.addEventListener('keydown', (e) => keyRove(e, { items: 'li', loop: true }), ); ``` Choose based on [where you configure the group](#which-to-reach-for). ## They fall back field by field `keyRove` reads the current settings on every keypress. An omitted option falls back to its attribute, then to the default: ```ts keyRove(e); // attributes and defaults keyRove(e, { loop: true }); // override looping only keyRove(e, { items: '[role="menuitem"]' }); // override item lookup only ``` `keys` falls back per action. With `{ keys: { next: 'KeyJ' } }`, J moves to the next item; every other action keeps its attribute or default binding. An empty, blank or comma-only binding also falls back. Use `'none'` to disable an action's key. ### Configuration differences | Setting | Attributes | Options | | --------------- | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | Items | `data-keyrove-item` marks each item. | `items` replaces that lookup with a selector or function that returns the items. | | Roots | `data-keyrove-root` marks each root. | `root` replaces that test with a selector for finding the nearest root. It is not a root element or boolean. | | Roving tabindex | `data-keyrove-roving-tabindex` enables roving on individual items. | `rovingTabindex` is one boolean for the group; `false` overrides item attributes. | | Skipping | `data-keyrove-skip` marks each skipped item. | `skip` replaces that test with a selector or predicate. Disabled items are always excluded from navigation. | | Move bindings | Each `data-keyrove-*-key` sets one action's binding. | `keys` overrides bindings per action; omitted or empty entries fall back. | | Focus shortcuts | `data-keyrove-focus-key` is scanned under the listener. Skipped and disabled targets are excluded. | `focusKeys` replaces the whole scan, including when it is `{}`. Explicit targets bypass skip checks, but disabled targets remain excluded. | | Typeahead label | `data-keyrove-typeahead` sets one item's label. | `label`, a `createTypeahead` option, is a function over every item. An empty result falls back to the attribute, then the item's text. | For example, these two options have different replacement rules: ```ts keyRove(e, { keys: { next: 'KeyJ' } }); // other actions still fall back keyRove(e, { focusKeys: { 'ctrl+KeyE': '#editor' } }); // no attribute scan ``` Roving tabindex also differs in scope. The attribute enrolls one item at a time, so mark every item that shares the tab stop. The option applies to the whole group: ```html title="Attributes" ``` ```ts title="Options" // // Every item shares the stop, whatever its roving attribute says. menu.addEventListener('keydown', (e) => keyRove(e, { items: 'li', rovingTabindex: true }), ); ``` Either way, [set an initial tab stop](/docs/examples/roving-tabindex#setting-the-initial-tab-stop). The [API reference](/docs/api#options) lists every option and its fallback. ## Which to reach for **Use attributes when you write the HTML.** Keep each group's settings beside its items. One delegated listener can serve [several groups](/docs/installation#several-groups-one-listener) or [nested roots](/docs/examples/nested-roots). Use the [attribute builders](/docs/api#attribute-builders) to type-check those settings in templates. A root configuration can also be passed directly to `keyRove` without renaming its fields: ```tsx import { itemAttributes, keyRove, rootAttributes } from '@mixedrays/keyrove'; import type { RootAttributeOptions } from '@mixedrays/keyrove'; const config = { loop: true, keys: { next: 'KeyJ', prev: 'KeyK' }, } satisfies RootAttributeOptions; // Settings in markup; the handler reads them on each keypress.