<div markdown="1">
  <sup>Using <a href="https://wangchujiang.com/#/app" target="_blank">my app</a> is also a way to <a href="https://wangchujiang.com/#/sponsor" target="_blank">support</a> me:</sup>
  <br>
  <a target="_blank" href="https://apps.apple.com/app/6758053530" title="Scap: Screenshot & Markup Edit for macOS"><img alt="Scap: Screenshot & Markup Edit" height="52" width="52" src="https://wangchujiang.com/appicon/scap.png"></a>
  <a target="_blank" href="https://apps.apple.com/app/6757317079" title="Screen Test for macOS"><img alt="Screen Test" height="52" width="52" src="https://wangchujiang.com/appicon/screen-test.png"></a>
  <a target="_blank" href="https://apps.apple.com/app/Deskmark/6755948110" title="Deskmark for macOS"><img alt="Deskmark" height="52" width="52" src="https://wangchujiang.com/appicon/deskmark.png"></a>
  <a target="_blank" href="https://apps.apple.com/app/Keyzer/6500434773" title="Keyzer for macOS"><img alt="Keyzer" height="52" width="52" src="https://wangchujiang.com/appicon/keyzer.png"></a>
  <a target="_blank" href="https://github.com/jaywcjlove/vidwall-hub" title="Vidwall Hub for macOS"><img alt="Vidwall Hub" height="52" width="52" src="https://wangchujiang.com/appicon/vidwall-hub.png"></a>
  <a target="_blank" href="https://apps.apple.com/app/VidCrop/6752624705" title="VidCrop for macOS"><img alt="VidCrop" height="52" width="52" src="https://wangchujiang.com/appicon/vidcrop.png"></a>
  <a target="_blank" href="https://apps.apple.com/app/Vidwall/6747587746" title="Vidwall for macOS"><img alt="Vidwall" height="52" width="52" src="https://wangchujiang.com/appicon/vidwall.png"></a>
  <a target="_blank" href="https://wangchujiang.com/mousio-hint/" title="Mousio Hint for macOS"><img alt="Mousio Hint" height="52" width="52" src="https://wangchujiang.com/appicon/mousio-hint.png"></a>
  <a target="_blank" href="https://apps.apple.com/app/6746747327" title="Mousio for macOS"><img alt="Mousio" height="52" width="52" src="https://wangchujiang.com/appicon/mousio.png"></a>
  <a target="_blank" href="https://apps.apple.com/app/6745227444" title="Musicer for macOS"><img alt="Musicer" height="52" width="52" src="https://wangchujiang.com/appicon/musicer.png"></a>
  <a target="_blank" href="https://apps.apple.com/app/6743841447" title="Audioer for macOS"><img alt="Audioer" height="52" width="52" src="https://wangchujiang.com/appicon/audioer.png"></a>
  <a target="_blank" href="https://apps.apple.com/app/6744690194" title="FileSentinel for macOS"><img alt="FileSentinel" height="52" width="52" src="https://wangchujiang.com/appicon/file-sentinel.png"></a>
  <a target="_blank" href="https://apps.apple.com/app/6743495172" title="FocusCursor for macOS"><img alt="FocusCursor" height="52" width="52" src="https://wangchujiang.com/appicon/focus-cursor.png"></a>
  <a target="_blank" href="https://apps.apple.com/app/6742680573" title="Videoer for macOS"><img alt="Videoer" height="52" width="52" src="https://wangchujiang.com/appicon/videoer.png"></a>
  <a target="_blank" href="https://apps.apple.com/app/6740425504" title="KeyClicker for macOS"><img alt="KeyClicker" height="52" width="52" src="https://wangchujiang.com/appicon/key-clicker.png"></a>
  <a target="_blank" href="https://apps.apple.com/app/6739052447" title="DayBar for macOS"><img alt="DayBar" height="52" width="52" src="https://wangchujiang.com/appicon/daybar.png"></a>
  <a target="_blank" href="https://apps.apple.com/app/6739444407" title="Iconed for macOS"><img alt="Iconed" height="52" width="52" src="https://wangchujiang.com/appicon/iconed.png"></a>
  <a target="_blank" href="https://apps.apple.com/app/6737160756" title="Menuist for macOS"><img alt="Menuist" height="52" width="52" src="https://wangchujiang.com/appicon/rightmenu-master.png"></a>
  <a target="_blank" href="https://apps.apple.com/app/6723903021" title="Paste Quick for macOS"><img alt="Quick RSS" height="52" width="52" src="https://wangchujiang.com/appicon/paste-quick.png"></a>
  <a target="_blank" href="https://apps.apple.com/app/6670696072" title="Quick RSS for macOS/iOS"><img alt="Quick RSS" height="52" width="52" src="https://wangchujiang.com/appicon/quick-rss.png"></a>
  <a target="_blank" href="https://apps.apple.com/app/6670167443" title="Web Serve for macOS"><img alt="Web Serve" height="52" width="52" src="https://wangchujiang.com/appicon/web-serve.png"></a>
  <a target="_blank" href="https://apps.apple.com/app/6503953628" title="Copybook Generator for macOS/iOS"><img alt="Copybook Generator" height="52" width="52" src="https://wangchujiang.com/appicon/copybook-generator.png"></a>
  <a target="_blank" href="https://apps.apple.com/app/6471227008" title="DevTutor for macOS/iOS"><img alt="DevTutor for SwiftUI" height="52" width="52" src="https://wangchujiang.com/appicon/devtutor.png"></a>
  <a target="_blank" href="https://apps.apple.com/app/6479819388" title="RegexMate for macOS/iOS"><img alt="RegexMate" height="52" width="52" src="https://wangchujiang.com/appicon/regex-mate.png"></a>
  <a target="_blank" href="https://apps.apple.com/app/6479194014" title="Time Passage for macOS/iOS"><img alt="Time Passage" height="52" width="52" src="https://wangchujiang.com/appicon/time-passage.png"></a>
  <a target="_blank" href="https://apps.apple.com/app/6478772538" title="IconizeFolder for macOS"><img alt="Iconize Folder" height="52" width="52" src="https://wangchujiang.com/appicon/iconize-folder.png"></a>
  <a target="_blank" href="https://apps.apple.com/app/6478511402" title="Textsound Saver for macOS/iOS"><img alt="Textsound Saver" height="52" width="52" src="https://wangchujiang.com/appicon/textsound-saver.png"></a>
  <a target="_blank" href="https://apps.apple.com/app/6476924627" title="Create Custom Symbols for macOS"><img alt="Create Custom Symbols" height="52" width="52" src="https://wangchujiang.com/appicon/create-custom-symbols.png"></a>
  <a target="_blank" href="https://apps.apple.com/app/6476452351" title="DevHub for macOS"><img alt="DevHub" height="52" width="52" src="https://wangchujiang.com/appicon/devhub.png"></a>
  <a target="_blank" href="https://apps.apple.com/app/6476400184" title="Resume Revise for macOS"><img alt="Resume Revise" height="52" width="52" src="https://wangchujiang.com/appicon/resume-revise.png"></a>
  <a target="_blank" href="https://apps.apple.com/app/6472593276" title="Palette Genius for macOS"><img alt="Palette Genius" height="52" width="52" src="https://wangchujiang.com/appicon/palette-genius.png"></a>
  <a target="_blank" href="https://apps.apple.com/app/6470879005" title="Symbol Scribe for macOS"><img alt="Symbol Scribe" height="52" width="52" src="https://wangchujiang.com/appicon/symbol-scribe.png"></a>
</div>
<hr>

# Hotkeys

[![Buy me a coffee](https://img.shields.io/badge/Buy_Me_a_Coffee-ffdd00?logo=buy-me-a-coffee&logoColor=black)](https://jaywcjlove.github.io/#/sponsor)
[![Follow On X](https://img.shields.io/badge/Follow%20on%20X-333333?logo=x&logoColor=white)](https://x.com/jaywcjlove)
[![](https://img.shields.io/npm/dm/hotkeys-js?logo=npm&label=)](https://www.npmjs.com/package/hotkeys-js)
[![](https://img.shields.io/github/stars/jaywcjlove/hotkeys-js.svg)](https://github.com/jaywcjlove/hotkeys/stargazers)
[![GitHub Actions CI](https://github.com/jaywcjlove/hotkeys-js/actions/workflows/ci.yml/badge.svg)](https://github.com/jaywcjlove/hotkeys-js/actions/workflows/ci.yml)
[![Coverage Status](https://jaywcjlove.github.io/hotkeys-js/coverage.svg)](https://jaywcjlove.github.io/hotkeys-js/lcov-report/index.html)
[![Chinese](https://jaywcjlove.github.io/sb/lang/chinese.svg)](https://wangchujiang.com/hotkeys-js/?lang=zh)
[![jaywcjlove/hotkeys-js](https://jaywcjlove.github.io/sb/ico/gitee.svg)](https://gitee.com/jaywcjlove/hotkeys)

HotKeys.js is an input capture library with some very special features, it is easy to pick up and use, has a reasonable footprint ([~8kB](https://bundlephobia.com/result?p=hotkeys-js)) (gzipped: **`3.8kB`**), and has no dependencies. It should not interfere with any JavaScript libraries or frameworks. Official document [demo preview](https://jaywcjlove.github.io/hotkeys-js), [compatibility test](https://jaywcjlove.github.io/hotkeys-js/dist/compatibility-test.html). [More examples](https://github.com/jaywcjlove/hotkeys-js/issues?q=label%3ADemo+).

```bash
╭┈┈╮          ╭┈┈╮  ╭┈┈╮
┆  ├┈┈..┈┈┈┈┈.┆  └┈╮┆  ├┈┈..┈┈┈┈┈..┈┈.┈┈..┈┈┈┈┈.
┆     ┆┆  □  ┆┆   ┈┤┆    < ┆  -__┘┆  ┆  ┆┆__ ┈┈┤
╰┈┈┴┈┈╯╰┈┈┈┈┈╯╰┈┈┈┈╯╰┈┈┴┈┈╯╰┈┈┈┈┈╯╰┈┈┈  ┆╰┈┈┈┈┈╯
                                  ╰┈┈┈┈┈╯
```

## Usage

You will need `Node.js` installed on your system.

```bash
npm install hotkeys-js --save
```

```js
import hotkeys from 'hotkeys-js';

hotkeys('f5', function(event, handler){
  // Prevent the default refresh event under WINDOWS system
  event.preventDefault()
  alert('you pressed F5!')
});
```

### Browser Usage

Or manually download and link **hotkeys.js** in your HTML. The library provides different formats for different use cases:

**CDN Links:** [UNPKG](https://unpkg.com/hotkeys-js/dist/) | [jsDelivr](https://cdn.jsdelivr.net/npm/hotkeys-js/) | [Githack](https://raw.githack.com/jaywcjlove/hotkeys/master/dist/) | [Statically](https://cdn.statically.io/gh/jaywcjlove/hotkeys/master/dist/)

**Available Formats:**

**IIFE (Immediately Invoked Function Expression) - Recommended for direct browser usage:**

```html
<script src="https://unpkg.com/hotkeys-js/dist/hotkeys-js.min.js">
</script>
<script type="text/javascript">
hotkeys('ctrl+a,ctrl+b,r,f', function (event, handler){
  switch (handler.key) {
    case 'ctrl+a': alert('you pressed ctrl+a!');
      break;
    case 'ctrl+b': alert('you pressed ctrl+b!');
      break;
    case 'r': alert('you pressed r!');
      break;
    case 'f': alert('you pressed f!');
      break;
    default: alert(event);
  }
});
</script>
```

**UMD (Universal Module Definition) - For CommonJS/AMD environments:**

```html
<script src="https://unpkg.com/hotkeys-js/dist/hotkeys-js.umd.cjs">
</script>
```

**ES Module - For modern browsers with module support:**

```html
<script type="module">
import hotkeys from 'https://unpkg.com/hotkeys-js/dist/hotkeys-js.js';
hotkeys('ctrl+a', function(event, handler){
  alert('you pressed ctrl+a!');
});
</script>
```

### Used in React

[react-hotkeys](https://github.com/jaywcjlove/react-hotkeys) is the React component that listen to keydown and keyup keyboard events, defining and dispatching keyboard shortcuts. Detailed use method please see its documentation [react-hotkeys](https://github.com/jaywcjlove/react-hotkeys).

[react-hotkeys-hook](https://github.com/JohannesKlauss/react-hotkeys-hook) - React hook for using keyboard shortcuts in components. Make sure that you have at least version 16.8 of react and react-dom installed, or otherwise hooks won't work for you.

## Browser Support

Hotkeys.js has been tested and should work in.

```shell
Internet Explorer 6+
Safari
Firefox
Chrome
```

## Supported Keys

HotKeys understands the following modifiers: `⇧`, `shift`, `option`, `⌥`, `alt`, `ctrl`, `control`, `command`, and `⌘`.

The following special keys can be used for shortcuts: backspace, tab, clear, enter, return, esc, escape, space, up, down, left, right, home, end, pageup, pagedown, del, delete, f1 through f19, num_0 through num_9, num_multiply, num_add, num_enter, num_subtract, num_decimal, num_divide.

`⌘` Command()  
`⌃` Control  
`⌥` Option(alt)  
`⇧` Shift  
`⇪` Caps Lock(Capital)  
~~`fn` Does not support fn~~  
`↩︎` return/Enter space  

## Defining Shortcuts

One global method is exposed, key which defines shortcuts when called directly.

```ts
declare interface HotkeysInterface extends HotkeysAPI {
  (key: string, method: KeyHandler): void;
  (key: string, scope: string, method: KeyHandler): void;
  (key: string, option: HotkeysOptions, method: KeyHandler): void;
  shift?: boolean;
  ctrl?: boolean;
  alt?: boolean;
  option?: boolean;
  control?: boolean;
  cmd?: boolean;
  command?: boolean;
}
declare interface HotkeysAPI {
  setScope: SetScope;
  getScope: GetScope;
  deleteScope: DeleteScope;
  getPressedKeyCodes: GetPressedKeyCodes;
  getPressedKeyString: GetPressedKeyString;
  getAllKeyCodes: GetAllKeyCodes;
  isPressed: IsPressed;
  filter: Filter;
  trigger: Trigger;
  unbind: Unbind;
  noConflict: NoConflict;
  keyMap: Record<string, number>;
  modifier: Record<string, number>;
  modifierMap: Record<string | number, number | string>;
}
```


```js
hotkeys('f5', function(event, handler) {
  // Prevent the default refresh event under WINDOWS system
  event.preventDefault();
  alert('you pressed F5!');
});

// Returning false stops the event and prevents default browser events
// Mac OS system defines `command + r` as a refresh shortcut
hotkeys('ctrl+r, command+r', function() {
  alert('stopped reload!');
  return false;
});

// Single key
hotkeys('a', function(event,handler){
  //event.srcElement: input
  //event.target: input
  if(event.target === "input"){
      alert('you pressed a!')
  }
  alert('you pressed a!')
});

// Key Combination
hotkeys('ctrl+a,ctrl+b,r,f', function (event, handler){
  switch (handler.key) {
    case 'ctrl+a': alert('you pressed ctrl+a!');
      break;
    case 'ctrl+b': alert('you pressed ctrl+b!');
      break;
    case 'r': alert('you pressed r!');
      break;
    case 'f': alert('you pressed f!');
      break;
    default: alert(event);
  }
});

hotkeys('ctrl+a+s', function() {
    alert('you pressed ctrl+a+s!');
});

// Using a scope
hotkeys('*','wcj', function(event){
  console.log('do something', event);
});
```

#### option

- `scope<String>`: Sets the scope in which the shortcut key is active
- `element<HTMLElement>`: Specifies the DOM element to bind the event to
- `keyup<Boolean>`: Whether to trigger the shortcut on key release
- `keydown<Boolean>`: Whether to trigger the shortcut on key press
- `splitKey<String>`: Delimiter for key combinations (default is `+`)
- `capture<Boolean>`: Whether to trigger the listener during the capture phase (before the event bubbles down)
- `single<Boolean>`: Allows only one callback function (automatically unbinds previous one)

```js
hotkeys('o, enter', {
  scope: 'wcj',
  element: document.getElementById('wrapper'),
}, function() {
  console.log('do something else');
});

hotkeys('ctrl-+', { splitKey: '-' }, function(e) {
  console.log('you pressed ctrl and +');
});

hotkeys('+', { splitKey: '-' }, function(e){
  console.log('you pressed +');
})
```

**keyup**

**key down** and **key up** both perform callback events.

```js
hotkeys('ctrl+a,alt+a+s', {keyup: true}, function(event, handler) {
  if (event.type === 'keydown') {
    console.log('keydown:', event.type, handler, handler.key);
  }

  if (event.type === 'keyup') {
    console.log('keyup:', event.type, handler, handler.key);
  }
});
```

## API REFERENCE

Asterisk "*"

Modifier key judgments

```js
hotkeys('*', function() {
  if (hotkeys.shift) {
    console.log('shift is pressed!');
  }

  if (hotkeys.ctrl) {
    console.log('ctrl is pressed!');
  }

  if (hotkeys.alt) {
    console.log('alt is pressed!');
  }

  if (hotkeys.option) {
    console.log('option is pressed!');
  }

  if (hotkeys.control) {
    console.log('control is pressed!');
  }

  if (hotkeys.cmd) {
    console.log('cmd is pressed!');
  }

  if (hotkeys.command) {
    console.log('command is pressed!');
  }
});
```

### setScope

Use the `hotkeys.setScope` method to set scope. There can only be one active scope besides 'all'.  By default 'all' is always active.

```js
// Define shortcuts with a scope
hotkeys('ctrl+o, ctrl+alt+enter', 'issues', function() {
  console.log('do something');
});
hotkeys('o, enter', 'files', function() {
  console.log('do something else');
});

// Set the scope (only 'all' and 'issues' shortcuts will be honored)
hotkeys.setScope('issues'); // default scope is 'all'
```

### getScope

Use the `hotkeys.getScope` method to get scope.

```js
hotkeys.getScope();
```

### deleteScope

Use the `hotkeys.deleteScope` method to delete a scope. This will also remove all associated hotkeys with it.

```js
hotkeys.deleteScope('issues');
```
You can use second argument, if need set new scope after deleting.

```js
hotkeys.deleteScope('issues', 'newScopeName');
```

### unbind

Similar to defining shortcuts, they can be unbound using `hotkeys.unbind`.

```js
// unbind 'a' handler
hotkeys.unbind('a');

// Unbind a hotkeys only for a single scope
// If no scope is specified it defaults to the current 
// scope (hotkeys.getScope())
hotkeys.unbind('o, enter', 'issues');
hotkeys.unbind('o, enter', 'files');
```

Unbind events through functions.

```js
function example() {
  hotkeys('a', example);
  hotkeys.unbind('a', example);

  hotkeys('a', 'issues', example);
  hotkeys.unbind('a', 'issues', example);
}
```

To unbind everything.

```js
hotkeys.unbind();
```

### isPressed

For example, `hotkeys.isPressed(77)` is true if the `M` key is currently pressed.

```js
hotkeys('a', function() {
  console.log(hotkeys.isPressed('a')); //=> true
  console.log(hotkeys.isPressed('A')); //=> true
  console.log(hotkeys.isPressed(65)); //=> true
});
```

### trigger

trigger shortcut key event

```js
hotkeys.trigger('ctrl+o');
hotkeys.trigger('ctrl+o', 'scope2');
```

### getPressedKeyCodes

Returns an array of key codes currently pressed.

```js
hotkeys('command+ctrl+shift+a,f', function() {
  console.log(hotkeys.getPressedKeyCodes()); //=> [17, 65] or [70]
})
```

### getPressedKeyString

Returns an array of key codes currently pressed.

```js
hotkeys('command+ctrl+shift+a,f', function() {
  console.log(hotkeys.getPressedKeyString()); 
  //=> ['⌘', '⌃', '⇧', 'A', 'F']
})
```

### getAllKeyCodes

Get a list of all registration codes.

```js
hotkeys('command+ctrl+shift+a,f', function() {
  console.log(hotkeys.getAllKeyCodes());
  // [
  //   { 
  //      scope: 'all', 
  //      shortcut: 'command+ctrl+shift+a', 
  //      mods: [91, 17, 16], 
  //      keys: [91, 17, 16, 65] 
  //    },
  //   { scope: 'all', shortcut: 'f', mods: [], keys: [42] }
  // ]
})
```

### filter

By default hotkeys are not enabled for `INPUT` `SELECT` `TEXTAREA` elements. `Hotkeys.filter` to return to the `true` shortcut keys set to play a role, `false` shortcut keys set up failure.

```js
hotkeys.filter = function(event){
  return true;
}
// How to add the filter to edit labels. 
// <div contentEditable="true"></div>
// "contentEditable" Older browsers that do not support drops
hotkeys.filter = function(event) {
  var target = event.target || event.srcElement;
  var tagName = target.tagName;
  return !(
    target.isContentEditable || 
    tagName == 'INPUT' || 
    tagName == 'SELECT' || 
    tagName == 'TEXTAREA'
  );
}

hotkeys.filter = function(event){
  var tagName = (event.target || event.srcElement).tagName;
  hotkeys.setScope(
    /^(INPUT|TEXTAREA|SELECT)$/.test(tagName) ? 'input' : 'other'
  );
  return true;
}
```

### noConflict

Relinquish HotKeys’s control of the `hotkeys` variable.

```js
var k = hotkeys.noConflict();
k('a', function() {
  console.log("do something")
});

hotkeys()
// -->Uncaught TypeError: hotkeys is not a function(anonymous function)
// @ VM2170:2InjectedScript._evaluateOn
// @ VM2165:883InjectedScript._evaluateAndWrap
// @ VM2165:816InjectedScript.evaluate @ VM2165:682
```

## Development

To develop, Install dependencies, Get the code:

```shell
$ git https://github.com/jaywcjlove/hotkeys.git
$ cd hotkeys     # Into the directory
$ npm install    # or  yarn install
```

To develop, run the self-reloading build:

```shell
$ npm run watch
```

Run Document Website Environment.

```shell
# Generate documentation website
$ npm run doc 
# Live-generate documentation website
$ npm run start 
```

To contribute, please fork Hotkeys.js, add your patch and tests for it (in the `test/` folder) and submit a pull request.

```shell
$ npm run test
$ npm run test:watch # Development model
```

## Contributors

As always, thanks to our amazing contributors!

<a href="https://github.com/jaywcjlove/hotkeys-js/graphs/contributors">
  <img src="https://jaywcjlove.github.io/hotkeys-js/CONTRIBUTORS.svg" />
</a>

Made with [action-contributors](https://github.com/jaywcjlove/github-action-contributors).

Special thanks to [@dimensi](https://github.com/dimensi) for the refactoring of version [4.0](https://github.com/jaywcjlove/hotkeys-js/issues/313).

## License

[MIT © Kenny Wong](./LICENSE)
