Keypress

A robust Javascript library for capturing keyboard input

© David Mauro 2014

~
`
!
1
@
2
#
3
$
4
%
5
^
6
&
7
*
8
(
9
)
0
_
-
+
=
backspace
tab
Q
W
E
R
T
Y
U
I
O
P
{
[
}
]
|
\
caps lock
A
S
D
F
G
H
J
K
L
:
;
"
'
enter
shift
Z
X
C
V
B
N
M
<
,
>
.
?
/
shift
ctrl
alt
cmd
cmd
alt
ctrl

 

print
scroll lock
pause break
insert
home
page up
delete
end
page down
num lock
/
*
-
7home
8
9pgup
+
4
5
6
1end
2
3pgdn
enter
0insert
.
del

Some browsers do not distinguish some or all of the numpad keys.

Why use Keypress?

Keypress is an input capture library with some very special features, it is easy to pick up and use, has a reasonable footprint (~9kb), and has no dependencies.

Here's some of what Keypress offers:

Bind to either or both keydown and keyup

While most of the time you'll want to bind to the keydown event to improve the perceived responsiveness, there are times when you'll need to bind to keyup in addition to keydown, or even exclusively.

The keyboard demo above demonstrates binding to both by depressing the key when keydown is triggered, and then releasing it when keyup is triggered. For some key combos or outlier keys (eg. pressing a non-modifier key while the command key is depressed) no keyup event is fired, in which case we manually trigger the keyup callback immediately after keydown.

Any keys you press on your keyboard should reflect on the keyboard above. Be default we are not preventing the default action for the events, so standard browser key shortcuts will still fire normally.

Arbitrary Modifiers

Move around the grid in 8 directions using the wasd keys.

With Keypress you can specify combos that include multiple non-modifer keys. In this demo we're using combinations of the wasd keys to create combos that allow us to move in diagonals without simply firing both components of the diagonal direction individually.

This allows for a very intuitive wasd tile-based movement system that allows for things like holding down one key of a component vector and tapping the other to repeatedly trigger the diagonal movement, rather than having to repeatedly press them both. We're using a combination of the is_exclusive property and the on_keyup events to achieve this.

Counting combos

You can navigate these tabs by holding the tab button down and pressing the spacebar repeatedly to cycle through them.

  • Home

    The first tab is very easy to get to by simply pressing the tab key once on it's own. This is great for something users will need to get back to often and quickly.

  • Portfolio

    You would probably want to arrange these by order of importance and how often you expect the user to get to them. This requires only pressing tab and space once.

  • About

    Here, have some lorem ipsum:
    Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

  • Contact

    Nothing to see here folks. You don't even get lipsum.

  • Another Option

    Yes, manila file folder, very tacky and silly, but you get it. I'm only trying to make a very obvious point about how you could use this feature.

  • Last One

    If the user continues to press space while holding tab, we'll wrap around to the start (we do that in our callback using modulo on the count integer that keypress passes to the callback as the second argument).

Counting combos are a unique feature that was created for fast and predictable navigation. For this demo, the tab key is used for menu navigation. Pressing it once will always take us back to the default menu position, but we can press the spacebar while holding down the tab key to jump through the options. The same number of keypresses will always take you to the same option no matter which one is currently activated.

As long as we're holding down the tab key, on each press of the spacebar, we'll call the keyDown callback with an additional count argument which is an integer letting us know how many times space has been pressed. This count is reset each time the last key of the combo, "tab", has been released.

Sequence Combos

Sequence combos are great if you want to hide an easter egg or some kind of cheat code. You can have multiple sequence combos without worrying about collisions (if one combo contained another for instance, they would both fire).

Write the word 'keypress' and the word 'JavaScript' (including proper capitalization) to see these in action. We've created sequence combos for the sequences "key", "keypress" and "JavaScript".

You can probably find other fun uses for sequence combos such as entering combos in a fighting game. Try making Ryu do a hadoken ("down", "right" then "x").

The combos will not register if the user takes too long to enter the combo. By default the user is given 800ms between each keypress for the combo to activate, but you can change that number to any ms value by setting the sequence_delay property of your Keypress Listener.

Other Features

Below in the documentation you can find details of all the options you can specify when creating a Keypress combo, but here's a quick synopsis of some more things you can do:

The prevent_default option in Keypress is special because it will preventDefault on all keys that are part of a combo, rather than only preventing when the full combo is pressed.

The prevent_repeat option makes sure your callback is only called once instead of being called every time the keydown event is fired.

You can enter special characters that can only be entered with the shift key (eg. !, @, #, $, etc.) as part of your combo and Keypress will seamlessly handle that for you.

You can bind your Keypress Listener to a particular DOM element if you only want to watch for combos when a particular element is active. Read more about that below in the advanced options.

Great! So how do I use it?

The first thing to do is include the JavaScript file in your page. Once you've got that loaded in, you'll want to start by instantiating a listener:

var listener = new window.keypress.Listener();

Once you've done that you can register combos with that listener you've created. The simplest way to do that is using the simple_combo API. It takes a space dilineated string or an array of strings of key names that describe your combo. The second parameter is the callback function that will get called every time the combo is entered by your users.

listener.simple_combo("shift s", function() {
    console.log("You pressed shift and s");
});

// There are also a few other shortcut methods:

// If we want to register a counting combo
listener.counting_combo("tab space", function(e, count) {
    console.log("You've pressed this " + count + " times.");
});

// If you want to register a sequence combo
listener.sequence_combo("up up down down left right left right b a enter", function() {
    lives = 30;
}, true);

If you only want to use Keypress for some very simple keyboard shortcuts, that's all you need to know!

Advanced Options

If you want to use some of the more advanced features of Keypress, you can use the register_combo API and supply an object with any number of options described below. All of the options are listed below with their default settings.

listener.register_combo({
    "keys"              : null,
    "on_keydown"        : null,
    "on_keyup"          : null,
    "on_release"        : null,
    "this"              : undefined,
    "prevent_default"   : false,
    "prevent_repeat"    : false,
    "is_unordered"      : false,
    "is_counting"       : false,
    "is_exclusive"      : false,
    "is_solitary"       : false,
    "is_sequence"       : false
});

If you are registering a lot of combos at once, you'll probably want to describe combo objects as described above, store them all in an array, and then register them all at once using the register_many API. That might look something like this:

var my_scope = this;
var my_combos = [
    {
        "keys"          : "shift s",
        "is_exclusive"  : true,
        "on_keydown"    : function() {
            console.log("You pressed shift and s together.");
        },
        "on_keyup"      : function(e) {
            console.log("And now you've released one of the keys.");
        },
        "this"          : my_scope
    },
    {
        "keys"          : "s",
        "is_exclusive"  : true,
        "on_keyup"      : function(event) {
            // Normally because we have a keyup event handler,
            // event.preventDefault() would automatically be called.
            // But because we're returning true in this handler,
            // event.preventDefault() will not be called.
            return true
        },
        "this"          : my_scope
    }
];
listener.register_many(my_combos);

You can then also unregister your combos either specifying the keys (beware that this will clear ALL combos that match the keys), or you can unregister by passing in the object you passed in to register_combo (another reason to manually create the combo objects yourself instead of using the simple_combo shortcut method). Or you can simply remove all registered combos by calling 'reset' on your listener.

// Remove all "shift s" combos we've registered
listener.unregister_combo("shift s");

// Or only these specific combos
listener.unregister_many(my_combos);

// Or remove ALL combos that have been registered
listener.reset();

If we simply want our listener to stop listening (for instance when focusing on a text input field or textarea). We could use listener.stop_listening() like so:

$('input[type=text]')
    .bind("focus", listener.stop_listening)
    .bind("blur", listener.listen);

Alternatively we might ONLY want to be listening when a particular DOM element is active. In that case, you'll probably want to bind your listener to a particular element. You can simply pass any element in as the first argument when creating a Keypress Listener. The second argument it takes is a defaults dictionary in case you want to completely override the defaults. That might look something like this:

var game_ele = document.getElementById("my_game_element_id");
var my_defaults = {
    is_unordered    : true,
    prevent_repeat  : true  
};
var listener = window.keypress.Listener(game_ele, my_defaults);

And here's a complete look at the Listener class' complete public API:

simple_combo(keys, on_keydown_callback); // Registers a very basic combo;
counting_combo(keys, on_count_callback); // Registers a counting combo
sequence_combo(keys, callback); // Registers a sequence combo
register_combo(combo_dictionary); // Registers a combo from a dictionary
register_many(combo_dictionary_array); // Registers an array of dictionaries
unregister_combo(keys_or_combo_dictionary); // Unregisters a single combo
unregister_many(array_of_keys_or_combo_dictionaries); // Unregisters many combos
get_registered_combos(); // Get a list of the combos registered with this listener
reset(); // Unregister all combos
listen(); // Begin listening. Listener is listening by default
stop_listening(); // Stop listening for combos until listen() is called again

A few important bits of info

If you want to make a shortcut to override default behavior such as saving, you should use the "meta" key instead of specifically using "ctrl" or "cmd". This "meta" key will become "ctrl" or "cmd" appropriately depending on the user's system.

Non-modifier keys do not ever fire a keyup event when the command key is held down, so we manually force a keyup event immediately after keydown is received. This means that if your combo includes the meta or command key, it can contain any number of modifier keys, but only one non-modifier key. Modifier keys are: "meta", "alt", "option", "ctrl", "shift", and "cmd". That means, for example, "meta shift s" is okay but "meta s p" is not.

Some keys have unreliable support and should mostly be avoided. Print, Scroll Lock, Pause/Break, and Insert have unreliable keyup or keydown firing in Windows or OS X, so I suggest avoiding them, although Keypress does still allow them.

The numpad keys should also be used with caution since you can't always count on the user having a numpad, and browser support for the numpad keys is not standardized. The numpad keys have to be specified manually with "num_" preceeding the key name. If you used a "." in your combo, for instance, it would assume a period and not a decimal (which can be used with the name "num_decimal").

For a full list of the key names, you should refer to the source code.

Support & Contact

This library has been tested with all major browsers and operating systems, but has not been thoroughly tested on non-English keyboards. If you run into any issues, please open an issue on GitHub.

Thank you

Any feedback is appreciated, and if you are using this library, I'd love to hear about it. Contact me on twitter as @dmaurolizer.