Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

react-keydown

glortho17.2kMIT1.9.12

Lightweight keydown wrapper for React components

react, react-component, decorator, higher-order-component, keypress, keydown, keyboard, keynav, key-navigation

readme

npm version dependencies

Use react-keydown as a higher-order component or decorator to pass keydown events to the wrapped component, or call methods directly via designated keys. Good for implementing keyboard navigation or other shortcuts.

Key advantages:

  • Declarative syntax: Components say what keys they will respond to.
  • Intuitive DX: Decorate a class or method to bind it to specified keys.
  • Scoping: Designate the scope of your bindings by decorating/wrapping components. Only those components and their children will receive the designated key events, and then only when they appear to be active.
  • Modifier keys: Support for standard modifier key combinations.
  • Lightweight: 2kb compressed and gzipped, and only attaches a single keydown listener to document, no matter how many keybindings you specify.
  • Cross-browser: Works in all browsers except IE 8 and below.

Consult the API & Reference Documentation or continue reading below for quick start.

NOTE: react-keydown doesn't use decorators itself, but to use the @keydown pattern in your application you will need a transpiler like Babel and a decorator transform plugin like this: https://github.com/loganfsmyth/babel-plugin-transform-decorators-legacy.

Install

npm install --save react-keydown

Use

The default build of react-keydown uses the CommonJS module system. For AMD or other support, use the umd-specific branch instead.

For methods: Decorate with keys that should trigger method

import React from 'react';
import keydown from 'react-keydown';

class MyComponent extends React.Component {

  @keydown( 'enter' ) // or specify `which` code directly, in this case 13
  submit( event ) {
    // do something, or not, with the keydown event, maybe event.preventDefault()
    MyApi.post( this.state );
  }
}

Note: Since the only context we have for keydown events is the component, decorated methods receive the event as their sole argument and the component instance as context.

Specify multiple keys that should trigger the method

import keydown, { Keys } from 'react-keydown';

const { ENTER, TAB } = Keys; // optionally get key codes from Keys lib to check against later

@keydown( ENTER, TAB, 'ctrl+z' ) // could also be an array
autocomplete( event ) {
  if ( event.which === ENTER ) { ... }
  MyApi.get( this.state );
}

For classes: Pass keydown events into your component

@keydown
class MyComponent extends React.Component {
  componentWillReceiveProps( { keydown } ) {
    if ( keydown.event ) {
      // inspect the keydown event and decide what to do
      console.log( keydown.event.which );
    }
  }

  render() {
    return (
      <div>keydown events will only get passed down after this DOM node mounts or is clicked on</div>
    );
  }
}

export default MyComponent;

Monitor only key codes which you care about:

const KEYS = [ 'shift+up', 'shift+down', 'enter', 'j', 'k', 'h', 'l' ];

@keydown( KEYS )
class MyComponent extends React.Component {
  ...
}

Use the @keydownScoped shortcut

When using the class decorator/higher-order component, decorate methods with @keydownScoped to identify the keydown.event prop as it comes in and bind certain values to methods:

import keydown, { keydownScoped } from 'react-keydown';

@keydown( 'enter' ) // optional to specify a key here. if called with just @keydown, all key events will get passed down
class MyComponent extends React.Component {
  render() {
    return <MyOtherComponent {...this.props} />;
  }
}

class MyOtherComponent extends React.Component {
  ...
  @keydownScoped( 'enter' ) // inspects nextProps.keydown.event in componentWillReceiveProps behind the scenes
  submit() {
    // submit
  }
}

This is a convenience method, but also lets you specify a larger view context where this key binding should be active. Sometimes the component where the binding is declared is too small on its own.

This can also be a good way to set up app-wide shortcuts. Wrap your root component with @keydown and then use @keydownScoped or manually inspect the keydown.event props in the child components where those bindings are relevant.

Handling all keys

In some cases you might want to handle all keys on your own. For that, you can specify the following:

import keydown, { ALL_KEYS } from 'react-keydown'

@keydown( ALL_KEYS )
handleKeys(ev) {
  // handle keys here
}

Handling all printable keys

Another useful feature is handling all printable characters.

import keydown, { ALL_PRINTABLE_KEYS } from 'react-keydown'

@keydown( ALL_PRINTABLE_KEYS )
beginEdit(ev) {
  // Start editing
}

Caveat: Input, textarea, and select elements

By default, bindings will not work when these fields have focus, in order not to interfere with user input and shortcuts related to these controls. You can override this in two ways:

  1. Give your shortcut a ctrl modifier.

  2. Since v1.6.0, there is experimental support for adding an onKeyDown binding to the element, specifying a method decorated with @keydown as the handler. For example:

class MyClass extends React.Component {

  @keydown( 'a' )
  myMethod( event ) {
    console.log( event ); // should log only on 'a' keystroke, whether input is focused or not
  }

  render() {
    return <input onKeyDown={ this.myMethod } />;
  }
}

In the second case you could make multiple inputs work this way by spreading { onKeyDown: this.myMethod } into them, or by making this a reusable input component that takes the method as a prop (or composes multiple methods passed in as props).

Demo

Go to the live demo or:

$ open example/public/index.html

Note that this is very much a work in progress!

Test

$ npm test

Notes, disclaimers, and tips

  • The decorator pattern @keydown currently requires transpilation by the Babel legacy decorators transform or the equivalent.
  • Components that have a React 16 fragment at their root may not be activated properly when clicked. See this issue for more detail.
  • The default build outputs CommonJS modules and native ES modules. For AMD or other support, use the umd-specific branch instead.
  • This lib has only been tested using ES2015 classes and class methods. Some method decoration functionality may work on other types of object methods.
  • Duplicate keybindings for components that are mounted at the same time will not both fire. The more recently mounted component, or the one that has been focused or clicked most recently, will win. If you do want both to fire, decorate a common ancestor class with @keydown( ... ) and then use @keydownScoped( ... ) in the child components (or just inspect nextProps.keydown.event in both).
  • Since the only context we have for keydown events is the component, decorated methods receive the event as their sole argument and the component instance as context.
  • The method decorators wrap React lifecycle methods in order to work as seamlessly and efficiently as possible. The class decorator does not do this, functioning instead as a higher-order component.

Questions

Why is this so limited, only working on keydown and such?

I published this out of my particular need on a project. If anyone else ever arrives here and needs something else let me know via issues or a pull request.

changelog

Change Log

All notable changes to this project will be documented in this file. This project adheres to Semantic Versioning.

1.9.12

  • Another security fix

1.9.11

  • Security fixes

1.9.10

  • Removes Symbol import from core-js, now that it is part of ECMAScript. See #91

1.9.9

  • Updates to core-js 3 and fixes tests. See #90

1.9.6

  • Suppress "Unable to find node on an unmounted component" error during shallow rendering in enzyme. See #76

1.9.5

  • Add pageUp, pageDown, home, end keys. See #72.

Thank you @dariuszpaluch!

1.9.4

  • Make sure Symbol is getting polyfilled. See #67

1.9.3

  • Remove smelly setTimeout from mount event handler now that we're capturing instead of bubbling and don't need to bump activation. See #64 and #66

1.9.2

  • Add ALL_PRINTABLE_CHARACTERS symbol for binding. See #61

1.9.0-1

  • [Fix] Added support for ALL_KEYS in decorators. See #56

1.7.8

  • [Fix] Reverted changes made in 1.7.5 to 1.7.7 & added click check in capture mode See #55 (comment)

1.7.5-7

  • [Fix] Do not reshuffle activation order of bound instances on click if the click target has already been removed from the DOM. See #55

1.7.4

  • [Fix] Check for existence of getAttribute before calling it, mainly so jsdom tests will pass (fixes #53)

1.7.3

  • [Fix] Actually fix the muting of keybindings when role=textbox + tests
  • [Fix] Fix tests

1.7.2

  • [Fix] Mute keybindings when role=textbox (fixes #51)

1.7.0

  • abdd314 [Feature] Add native es modules build (fixes #47)

1.6.9

  • 83df6fd [Fix] Prevent double firing of onKeyDown handlers when one of the bindings already has a ctrl modifier (which fires in text fields already)

1.6.7

  • b015d8b [Fix] Better Symbol type checking. Thank you @AntonovAv (fixes #42).

1.6.5

  • f6c6c01 [Feature] Patch feature: add "delete" and "del" as aliases to key code 46.
  • 3870148 [Fix] Return values from decorated methods (fixes #35)
  • f694b33 [Fix] Use window.KeyboardEvent instead of just KeyboardEvent for better clarity and testability with jsdom. (fixes #35)

1.6.3

  • 7f322a5 [Fix] Use Array.from polyfill (fixes #29 and #33)
  • 065f313 [Maintenance] Switching to Yarn

1.6.2

  • 146920a [Fix] Avoid transpilation to Array.from (thanks to @elyobo for pr #27)
  • 3a6821f [Fix] Do not use Array.fill in order to maximize compatibility with IE (thanks to @elyobo for pr #26)

1.6.1

  • f3a8ed0 [Fix] 1.6.0 introduced a bug wherein a decorated method would not fire when called programmatically.
  • 8aa893f [Maintenance] Switch to simple indices rather than uuids (fixes #19 via pr #24 from @jeffijoe)

1.6.0

  • e0cfb05 [Feature] Add experimental support for using the @keydown method decorator as a filter for handlers bound via onKeyDown to input, textarea, etc.

1.5.0

  • 91209b0 [Feature] Add function key codes

1.4.9

  • 6d00ef9 [Fix] Correct version typo for react-dom in package.json (fixes #21)

1.4.8

  • 025e549 [Fix] Use UUIDs as keys in the bindings map rather than the prototypes themselves, to address HMR issues in webpack (see #16)

1.4.7

  • React >= 0.14.0 rather than ^0.14.0

1.4.6

1.4.5

  • Wrong react version dependency was cached.

1.4.4

  • 8c00bb2 [Fix] Use proper es6 module import syntax

1.4.1

  • 7fb3e85 [Fix] Add uppercase version of special keys back in for backwards compatibility

1.4.0

  • 699f42c [Dependency Update] Switch to react-dom for full compatibility with React 0.14+. (Breaking SemVer just slightly for the sake of version parity.)
  • ac8fb21 [Fix] Fix scoping problems introduced in last major code reorganization that meant some mounted instance would always receive keydown events.

1.3.7

  • d1453b5 [Fix] Add node check to safeguard against unmounted components when looking for ancestors of click event targets.

1.3.6

  • ec0bff2 [Fix] Re-export Keys lib after reorganization.
  • 1b93de3 [Maintenance] Reorganize code to be more coherent/modular/maintainable.
  • 301f4e4 [Maintenance] Write preliminary reference documentation.
  • 79af533 [Feature] Add support for more alphanumeric keys.
  • c46805e [Maintenance] Move compile task into script to more easily manage different builds for umd vs. commonjs-only (default).

1.3.5