A simple micro-library for defining and dispatching keyboard shortcuts with no dependencies
npx @tessl/cli install tessl/npm-hotkeys-js@3.13.0Hotkeys-js is a simple micro-library for defining and dispatching keyboard shortcuts in web applications. It provides a comprehensive API for capturing keyboard events with support for modifier keys, key combinations, scopes for context-specific shortcuts, and flexible filtering options.
npm install hotkeys-jsES Modules:
import hotkeys from 'hotkeys-js';CommonJS:
const hotkeys = require('hotkeys-js');Browser (CDN):
<script src="https://unpkg.com/hotkeys-js/dist/hotkeys.min.js"></script>import hotkeys from 'hotkeys-js';
// Simple shortcut
hotkeys('ctrl+a', function (event, handler) {
console.log('Select all triggered');
});
// Multiple key combinations
hotkeys('ctrl+s, command+s', function (event, handler) {
event.preventDefault();
console.log('Save triggered');
});
// With scope for context-specific shortcuts
hotkeys('enter', 'editing', function (event, handler) {
console.log('Enter pressed in editing mode');
});
// Set active scope
hotkeys.setScope('editing');Hotkeys-js is built around several key architectural components:
Define keyboard shortcuts with flexible options for key combinations, scopes, and handler functions.
/**
* Register a keyboard shortcut with a handler function
* @param key - Key combination string (e.g., 'ctrl+s', 'f1', 'ctrl+shift+a')
* @param method - Handler function called when shortcut is triggered
*/
hotkeys(key: string, method: KeyHandler): void;
/**
* Register a keyboard shortcut with a specific scope
* @param key - Key combination string
* @param scope - Scope name for context-specific shortcuts
* @param method - Handler function called when shortcut is triggered
*/
hotkeys(key: string, scope: string, method: KeyHandler): void;
/**
* Register a keyboard shortcut with advanced options
* @param key - Key combination string
* @param options - Configuration options for the shortcut
* @param method - Handler function called when shortcut is triggered
*/
hotkeys(key: string, options: Options, method: KeyHandler): void;Manage different contexts for keyboard shortcuts, allowing the same keys to perform different actions in different application states.
/**
* Set the current active scope. Only shortcuts in 'all' and active scope will trigger
* @param scopeName - Name of the scope to activate
*/
setScope(scopeName: string): void;
/**
* Get the current active scope name
* @returns The current scope name (defaults to 'all')
*/
getScope(): string;
/**
* Delete a scope and all associated shortcuts
* @param scopeName - Name of the scope to delete
* @param newScopeName - Optional new scope to set after deletion
*/
deleteScope(scopeName: string, newScopeName?: string): void;Check the current state of pressed keys and retrieve information about active shortcuts.
/**
* Check if a specific key is currently pressed
* @param keyCode - Key code number or key string to check
* @returns True if the key is currently pressed
*/
isPressed(keyCode: number | string): boolean;
/**
* Get array of key codes currently pressed
* @returns Array of numeric key codes for currently pressed keys
*/
getPressedKeyCodes(): number[];
/**
* Get array of key string representations currently pressed
* @returns Array of string representations of currently pressed keys
*/
getPressedKeyString(): string[];
/**
* Get list of all registered shortcut key combinations with their metadata
* @returns Array of registered shortcuts with scope, shortcut, mods, and keys
*/
getAllKeyCodes(): Omit<HotkeysEvent, 'method' | 'key'>[];Control shortcut bindings and programmatically trigger shortcuts.
/**
* Remove all keyboard shortcut bindings
*/
unbind(): void;
/**
* Remove keyboard shortcut bindings for a specific key
* @param key - Key combination to unbind
*/
unbind(key: string): void;
/**
* Remove keyboard shortcut bindings for a key in a specific scope
* @param key - Key combination to unbind
* @param scopeName - Scope name to unbind from
*/
unbind(key: string, scopeName: string): void;
/**
* Remove a specific handler for a key in a specific scope
* @param key - Key combination to unbind
* @param scopeName - Scope name to unbind from
* @param method - Specific handler function to remove
*/
unbind(key: string, scopeName: string, method: KeyHandler): void;
/**
* Remove a specific handler for a key
* @param key - Key combination to unbind
* @param method - Specific handler function to remove
*/
unbind(key: string, method: KeyHandler): void;
/**
* Programmatically trigger a keyboard shortcut
* @param shortcut - Shortcut string to trigger
* @param scope - Optional scope to trigger within
*/
trigger(shortcut: string, scope?: string): void;
/**
* Filter function to determine if hotkeys should be active for given events
* Can be overridden to customize filtering behavior
* @param event - Keyboard event to filter
* @returns True if hotkeys should be active, false otherwise
*/
filter(event: KeyboardEvent): boolean;Additional utility functions for library management and conflict resolution.
/**
* Release control of global 'hotkeys' variable and return hotkeys reference
* @param deep - Optional flag for deep conflict resolution
* @returns Reference to the hotkeys function
*/
noConflict(deep?: boolean): Hotkeys;Boolean properties indicating the current state of modifier keys.
hotkeys.shift: boolean; // Shift key state
hotkeys.ctrl: boolean; // Ctrl key state
hotkeys.alt: boolean; // Alt key state
hotkeys.option: boolean; // Option key state (alias for alt)
hotkeys.control: boolean; // Control key state (alias for ctrl)
hotkeys.cmd: boolean; // Command key state
hotkeys.command: boolean; // Command key state (alias for cmd)Objects containing mappings between key names and key codes.
hotkeys.keyMap: Record<string, number>; // Map of key names to key codes
hotkeys.modifier: Record<string, number>; // Map of modifier key names to codes
hotkeys.modifierMap: Record<string, number | string>; // Bidirectional modifier mappinginterface HotkeysEvent {
key: string; // The pressed key
keys: number[]; // Array of pressed key codes
method: KeyHandler; // The handler function
mods: number[]; // Modifier key codes
scope: string; // Current scope
shortcut: string; // The shortcut string
}
type KeyHandler = (keyboardEvent: KeyboardEvent, hotkeysEvent: HotkeysEvent) => void | boolean;
type Options = {
scope?: string; // Scope name
element?: HTMLElement | null; // Target element
keyup?: boolean | null; // Listen to keyup events
keydown?: boolean | null; // Listen to keydown events
capture?: boolean; // Use capture phase
splitKey?: string; // Key combination separator
single?: boolean; // Single callback only
}leftrightupdownarrowuparrowdownarrowleftarrowrightf1f19backspacedeletedelinsertinshomeendpageuppagedowntabenterreturnescescapespaceclearcapslocknum_0num_9num_multiplynum_addnum_enternum_subtractnum_decimalnum_divideshift⇧altoption⌥ctrlcontrol⌃cmdcommandmeta⌘Use
+ctrl+sctrl+shift+zcommand+option+iUse
,ctrl+s, command+s// Define shortcuts with different scopes
hotkeys('ctrl+z', 'editing', function() {
console.log('Undo in editing mode');
});
hotkeys('ctrl+z', 'viewing', function() {
console.log('Undo in viewing mode');
});
// Switch between scopes
hotkeys.setScope('editing'); // Only 'all' and 'editing' shortcuts active
hotkeys.setScope('viewing'); // Only 'all' and 'viewing' shortcuts active// Listen only to keyup events
hotkeys('space', { keyup: true }, function(event, handler) {
console.log('Space released');
});
// Target specific element
hotkeys('enter', {
element: document.getElementById('search-input'),
scope: 'search'
}, function(event, handler) {
console.log('Enter in search input');
});// Customize filtering to allow hotkeys in contentEditable elements
hotkeys.filter = function(event) {
const target = event.target || event.srcElement;
const tagName = target.tagName;
// Allow hotkeys in contentEditable but not in inputs
return !(tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA');
};hotkeys('ctrl+shift+d', function(event, handler) {
// Check what other keys are pressed
const pressedCodes = hotkeys.getPressedKeyCodes();
const pressedKeys = hotkeys.getPressedKeyString();
console.log('Pressed key codes:', pressedCodes);
console.log('Pressed keys:', pressedKeys);
// Check specific key
if (hotkeys.isPressed('m')) {
console.log('M key is also pressed');
}
});