# Listen key combinations with JavaScripts

Here’s how to listen to more complex keyboard input with JavaScript.

## A basic example

Usually, it’s better to listen `keyup`, because with `keydown` it’s easy to
unleash a deluge of events possibly bogging up your site.

```js
const handleKeyboard = event => {
  if (event.key === '/') console.log('The ’/’ key was pressed')
}

document.addEventListener('keyup', handleKeyboard)
```

## Anatomy of the KeyboardEvent

This is roughly what the event contains when an alt key was pressed:

```js
{
  altKey: false,
  ctrlKey: false,
  metaKey: false,
  shiftKey: false,
  charCode: 0,
  code: "AltLeft",
  isComposing: false,
  key: "Alt",
  keyCode: 18,
  location: 1,
  repeat: false,
  type: "keyup",
  which: 18,
  // Unrelated properties redacted for brevity.
}
```

Here’s a quick rundown what each of them do:

`KeyboardEvent.altKey`

A boolean telling you if an alt key was pressed when the event
    was initiated.

`KeyboardEvent.ctrlKey`

A boolean telling you if an ctrl key was pressed when the event
    was initiated.

`KeyboardEvent.metaKey`

A boolean telling you if a Meta key was pressed when the event
    was initiated. In Mac this would be ⌘ Command and in Windows it
    would be the ⊞ Windows key.

`KeyboardEvent.shiftKey`

A boolean telling you if a shift key was pressed when the event
    was initiated.

`KeyboardEvent.charCode` 👎

Deprecated, but it returns the Unicode value of the character pressed.

`KeyboardEvent.code`

Returns the name of a physical key on the keyboard, not the character
    generated by pressing a key, meaning it’s immune to keyboard layouts or
    modifier keys. You can get really granular with this, for example you can
    know which alt key was pressed for example `AltLeft` or
    `AltRight`

`KeyboardEvent.isComposing`

A boolean value indicating whether the event was fired during an open
    compositions session e.g: between `compositionstart` and
    `compositionend`. Composition session is when a character is
    required to be entered using multiple keystrokes, like Chinese characters or
    accented characters like "à": alt+` followed by an a.

`KeyboardEvent.key`

Returns the name of the key pressed, including the state of any modifier
    keys.

`KeyboardEvent.keyCode` 👎

Deprecated, but it’s still widely used (as of writing this). It returns the
    numerical value of of the key.

`KeyboardEvent.location`

The location can have 4 values: 0, 1, 2, and 3. The location is
    **0** if a key has only one version (i.e. "g"),
    **1** if it was the left-hand version of a key (like
    alt), **2** if right-hand side, and
    **3** if the key is part of a numeric keypad.

`KeyboardEvent.repeat`

A boolean property signaling whether the key is pressed in during a
    `keydown` event.

`KeyboardEvent.shiftKey`

A boolean telling if a shift key was pressed when the event was
    initiated.

`KeyboardEvent.type`

**Not purely a keyboard event property**. A string value simply
    representing the event name. Handy when listening to multiple events at the
    same time.

## Listening key combos: Cmd or Ctrl + key

I’m using Cmd+/ (or Ctrl+/) as an example here (which
opens the search on this site btw). When doing key-combos it’s good to listen to
the `keydown` event:

```js
const handleKeyboard = ({ repeat, metaKey, key }) => {
  if (repeat) return
  if ((metaKey || ctrlKey) && key === '/') console.log('’Cmd+/’ was pressed')
}

document.addEventListener('keydown', handleKeyboard)
```

See the bail on the `repeat`, without that, there would be a torrential flood of
events firing when the key is kept in a pressed state, which might bog up your
app.

## Key-combo example using React

This little demo toggles a search bar on the header with Cmd+/ or

Ctrl+/:

```jsx
const [searchOpen, setSearchOpen] = React.useState(false)

const handleKeyboard = ({ repeat, metaKey, ctrlKey, key }) => {
  if (repeat) return

  // Handle both, `ctrl` and `meta`.
  if ((metaKey || ctrlKey) && key === '/') setSearchOpen(prev => !prev)
}

React.useEffect(() => {
  document.addEventListener('keydown', handleKeyboard)

  // Important to remove the listeners.
  return () => document.removeEventListener('keydown', handleKeyboard)
})
```

Notes on the above code:

- Remember to handle both `metaKey` and `ctrlKey` so it works on Windows and
  Mac.
- Also remove the events when you’re done with them: using function components
  you return a function and run the cleanup inside that. It’s equivalent to the
  good old `componentWillUnmount` method.

[CodePen](https://codepen.io/hilja/pen/ZEOdZGz)

## Listening to multiple keys that are not meta, ctrl, or alt

If you want to, say, open the search when s and e are
pressed, you need to be a bit more clever. You can keep a log of the keys
pressed on `keydown` and then clear the log on `keyup`:

```js
const keyLog = {}
const handleKeyboard = ({ type, key, repeat, metaKey }) => {
  if (repeat) return

  if (type === 'keydown') {
    keyLog[key] = true
    // When "s" and "e" are pressed.
    if (keyLog.s && key === 'e') setSearchOpen(prev => !prev)
  }

  // Remove the key from the log on keyup.
  if (type === 'keyup') delete keyLog[key]
}

React.useEffect(() => {
  const events = ['keydown', 'keyup']
  events.forEach(name => document.addEventListener(name, handleKeyboard))

  return () =>
    events.forEach(name => document.removeEventListener(name, handleKeyboard))
})
```

Here’s a demo for that one:

[CodePen](https://codepen.io/hilja/pen/zYBVQBp)
