Editable targets
Mix keyboard navigation with text fields, selects and sliders. Editable controls keep their native keys while the arrows move between the rows around them.
Movement bindings leave editable fields their native keys. Arrows can move a caret or change a value, and letter bindings do not capture typed text. Modified focus shortcuts are the exception.
Arrow between the demo's rows, then Tab into a control. Text fields keep their editing behavior, Font size keeps its slider keys, and ↓ on the checkbox moves to the next row. The readout shows navigation in indigo and keys left to the browser in gray.
<ul id="settings">
<li data-keyrove-item tabindex="0">
<label>
<input type="checkbox" checked />
Email notifications
</label>
</li>
<li data-keyrove-item tabindex="0">
<label>
Display name
<input type="text" value="Ada" />
</label>
</li>
<li data-keyrove-item tabindex="0">
<label>
Font size
<input type="range" min="12" max="24" value="16" />
</label>
</li>
<li data-keyrove-item tabindex="0">
<label>
<input type="checkbox" />
Weekly digest
</label>
</li>
<li data-keyrove-item tabindex="0">
<label>
Signature
<textarea rows="2">Sent from my keyboard</textarea>
</label>
</li>
</ul>
import { keyRove } from '@mixedrays/keyrove';
document
.querySelector('#settings')
.addEventListener('keydown', (e) => keyRove(e));
No extra configuration is needed. A row remains the current item while focus is inside one of its controls. When navigation resumes, it starts from that row.
Which elements are editable
The test is on the event's target and its ancestors:
- a
textareaor aselect; - a
[contenteditable]element and everything inside it, except acontenteditable="false"island, which opts back out; - an
inputof any type other thanbutton,checkbox,color,file,image,resetandsubmit.
The input list is drawn by what the bound keys do natively. Arrows move a
caret in a text field, step a number, slide a range and move between
radio buttons; on a checkbox or a button they do nothing, so navigating from
one takes nothing away. That is why ↓ on
Email notifications moves rows while ↓ on Font size
nudges the slider.
By target, not by key
The target determines whether movement keys are handled. In a text field,
ctrl+ArrowRight still moves by word and KeyJ still types "j", even when
those combos are bound to navigation. keyRove returns null, so any handler
chained after it also receives the event.
The one exception
A focus key with Ctrl, Alt or Meta can move focus from an editable field. Bare keys and Shift-only combinations remain available for typing. Choose shortcuts carefully: some modifiers also produce text, including AltGr reported as Ctrl+Alt.
Typeahead never captures typing inside a field.
While an input method composes
While isComposing is true, keyrove leaves every key to the input method,
including modified focus shortcuts. This lets users navigate candidates and
finish entering text.
Rows as tab stops
Each row here carries tabindex="0" so it can be focused apart from its
control, which is what lets a row be the position before
Tab reaches the control inside. It also doubles the tab
stops. Put roving tabindex on the rows and
the list costs one stop, with the controls keeping theirs. Or make the controls
the items, <input data-keyrove-item type="checkbox">, and the rule reads the
same way round: the checkboxes navigate, the text fields do not.