Hotkeys
Method grab
return Hotkey
Start listening to a hotkey. Specify hint and callback to be triggered on hit. Returns a
Hotkey object. It accepts following parameters:
-
hotkey(string) Key combination to be grabbed. Either use anycodevalue https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_code_values of the event:wh.grab("Digit1", "Hint", () => alert("Digit1 key (whatever keyboard layout) hit")) wh.grab("Alt+ArrowDown", "Hint", () => alert("Alt+ArrowDown hit")) wh.grab("KeyF", "Hint", () => alert("Litter f hit (specified by the `code` property)"))Or use any
keyvaluewh.grab("1", "Hint", () => alert("Number 1 hit")) wh.grab("+", "Hint", () => alert("Plus sign hit")) wh.grab("?", "Hint", () => alert("Question mark hit (you don't have to mention shift in the hotkey)")) wh.grab("Alt++", "Hint", () => alert("Plus sign with the Alt hit")) wh.grab("f", "Hint", () => alert("Letter f hit (specified by the `key` property)"))If you define both
codeandkey, thecodetakes precedence, and then the last one defined takes precedence.Handling conflicts: If multiple hotkeys are defined, only the last one defined is executed (unless it returns
false, in which case the next one is tried). We skip hotkeys that are not in the correct scope or whose underlying element is disabled or hidden. UsegetConflictsor thewarnConflictsoption to spot the duplicates.Possible modifiers are:
Alt,Shift,Ctrl(Control),Meta(Cmd) andMod.Modis the "the usual one" modifier – it becomesMeta(⌘) on a Mac andCtrlanywhere else, so a single definition fits every platform:On a Mac, the hints are rendered with the Apple symbols (
⌘⇧k). Set themacoption tofalse(ortrue) to override the autodetection.These friendlier spellings are accepted too:
Return(=Enter),Esc,Del,Ins,Up,Down,Left,Right,PgUp,PgDown.Sequences. Separate the combinations by a space and the hotkey only fires when they are typed one after another (within
sequenceTimeout, one second by default). The prefix key is swallowed while the sequence is pending, so it does not leak to the page.wh.grab("g i", "Go to issues", () => location.assign("/issues")) wh.grab("Ctrl+k Ctrl+s", "Keyboard settings", openSettings) wh.grab("Shift Shift", "Search everywhere", openSearch) // a lone modifier tapped twiceA sequence beats a plain hotkey ending with the same key –
istill works on its own,g iwins whengpreceded it.A plain hotkey starting with the same key as a sequence is held back for
sequenceTimeout, in case the sequence is what the user is actually typing – grab bothgandg iand pressinggalone still fires the plain hotkey, just after that short delay; pressinggthenifires the sequence instead, with no delay perceptible forg iitself.Keyboard layouts. A multi-character name is a
code(a physical key, layout independent), a single character is akey(the character produced, layout dependent) – see Keyboard layouts.The numeric keypad falls back to the main row: a hotkey grabbed as
Digit1orEnterfires fromNumpad1/NumpadEnteras well, unless another hotkey claims the numpad code explicitly.Text-input guard. We try to determine whether a hotkey should not be triggered - for example, when pressing keys like
aorDeleteinside an<input>field, which wouldn't make sense. This guards plain letter keys, text-navigation keys (arrows, Home/End, Delete, Backspace) and Enter/Tab while focus is inside a form field orcontenteditable.Escape, function keys (F2, ...) andCtrl/Alt/Metacombinations are not guarded - they still fire inside a text field.Some special hotkeys like
*Ctrl+PageDownwill likely never be passed to the webpage and therefore do not function. *hint(string): Text shown in the help dialog and hint badges. *action({string|HTMLElement|Function}): What will happen on hotkey trigger. * If action returns false, hotkey will be treated as non-existent and event will propagate further. * If action is a HTMLElement or its string selector, its click or focus method (form elements) is invoked instead. *scope({HTMLElement|string|Function}|{scope, inInput, inHidden, group}): Scope within the hotkey is allowed to be launched, or an options object. * The scope can be an HTMLElement that the active element is being search under when the hotkey triggers. * The scope can an HTMLElement selector, does not have to exist at the shorcut definition time. * The scope can be a function, resolved at the keystroke time. True means the scope matches. That way, you can implement negative scope. * (Ex: down arrow should work unless there is DialogOverlay in the document root.) * Pass{scope, inInput: true}instead to fire the hotkey even while the user is typing into an input or a contenteditable (ex: arrow keys navigating a combobox's suggestion list):groupadds the hotkey to a namedHotkeyGroup, same as passing it throughwh.group(name, [[...]]):
Method group
return HotkeyGroup
Grab multiple hotkeys at once. Returns a HotkeyGroup that you may call methods enable, disable, toggle(enable=null) on.
// name, definitions (grab method parameters as a list)
const general = wh.group("General hotkeys", [
["n", "Next", () => this.nextFrame()],
["p", "Prev", () => this.previousFrame()],
])
general.disable() // disable all hotkeys
general.toggle() // re-enable them
general.toggle(false) // re-disable them
Alternatively, you can set the group in the DOM. If not changed by groupAttribute), use the [data-hotkey-group] attribute either on the element or on any of its ancestors:
<a href="..." data-hotkey="Alt+1" data-hotkey-group="Global shortcuts" title="Go to an example link 1">link 1</a>
<div data-hotkey-group="Global shortcuts">
<a href="..." data-hotkey="Alt+2" title="Go to an example link 2">link 2</a>
</div>
Such group can be accessed via the method group too: