When building games or any application that requires quick reactions from the
user, a system to track key states is needed. You might say "Silly developer!
There are events for that! They're called keydown
and keyup
!" This is
correct, but the problem that Keydrown solves is more subtle: When you press
and hold any key on the keyboard, there is a brief delay between the initial
firing of the keydown
event handler and the subsequent firings of that event
for every tick. Here's an approximate ASCII visualization:
TIME (seconds) KEYDOWN HANDLER FIRING STATE
-----------------------------------------------------
0 Firing
0.25 Not firing
0.50 Not firing
0.75 Not firing
1 Firing
1.25 Firing
1.50 Firing
1.75 Firing
2 Firing
...And the handler will just keep firing until the button is released. The
expectation from the user is that the key handler would be firing for the
entire duration of time that the key is held down - the early ticks where the
keydown
state is not handled creates a feeling of sluggishness and noticeably
worsens the User Experience. A way around this delay is to only listen for one
keydown
event for a button, and execute the key handler on every tick until
the corresponding keyup
event is detected for that button.
Keydrown makes this super easy.
All Keydrown functionality exists under the kd
namespace.
Every letter key, as well as some other keys on the keyboard are represented in
a map of kd.Key
instances with uppercase key names:
kd.A instanceof kd.Key; // true
kd.SPACE instanceof kd.Key; // true
kd.UP instanceof kd.Key; // true
You can see the full list of supported keys in
kd.map.js
(more key codes can easily be added, please submit a Pull Request if you add
more).
kd.Key
has the following API:
/**
* @param {function=} opt_handler
*/
kd.Key.prototype.down = function (opt_handler)
opt_handler
fires for every tick where the key is held down. There is
no early delay, as described in the ASCII graph above. Calling this method for
a key again will overwrite the previous opt_handler
- only one handler
function is allowed per key.
If opt_handler
is omitted, this function invokes whatever handler function
was previously bound with kd.Key#down
.
/**
* @param {function=} opt_handler
*/
kd.Key.prototype.up = function (opt_handler)
opt_handler
fires when the key is released by the user. As with
kd.Key#down
, only one handler function is allowed. Unlike kd.Key#down
,
opt_handler
does not fire continuously — only once when the key is released.
If opt_handler
is omitted, this function invokes whatever handler function
was previously bound with kd.Key#up
.
/**
* @param {function=} opt_handler
*/
kd.Key.prototype.press = function (opt_handler)
opt_handler
fires once when the key is pressed by the user. Only one handler
function is allowed. This is not a repeating state — it only fires once until
the user releases the key and presses it again.
If opt_handler
is omitted, this function invokes whatever handler function
was previously bound with kd.Key#press
.
kd.B.down(function () {
console.log('The "B" key is being held down!');
});
kd.B.up(function () {
console.log('The "B" key was released!');
});
kd.SPACE.press(function () {
console.log('The space bar was pressed!');
});
kd.Key.prototype.unbindDown = function ()
Unbinds the function handler that was bound with kd.Key#down
.
kd.Key.prototype.unbindUp = function ()
Unbinds the function handler that was bound with kd.Key#up
.
kd.Key.prototype.unbindPress = function ()
Unbinds the function handler that was bound with kd.Key#press
.
kd.B.down(function () {
console.log('The "B" key is being held down!');
});
// Now pressing the "B" key won't do anything
kd.B.unbindDown();
The kd
Object has helper methods attached to it, and they are represented by
camelCase property names.
kd.tick = function ()
Check the states of all of the keys and invoke the necessary key handlers. You should call this once and only once somewhere in your run loop. If you don't have this somewhere in your run loop, Keydrown won't do anything.
/**
* @param {function} handler
*/
kd.run = function (handler)
A basic run loop. If your application already has a run loop, you don't need
this. kd.run
uses requestAnimationFrame
if it is available, and falls back
to a setTimeout
loop if it is not.
kd.stop = function ()
Cancels the run loop started by kd.run
.
kd.SPACE.down(function () {
console.log('The space bar is being held down!');
});
kd.ESC.down(function () {
console.log('Canceling the loop.');
kd.stop();
});
kd.run(function () {
kd.tick();
});
If you want to keep things simple, all you need is either dist/keydrown.js
or
dist/keydrown.min.js
from this Git repo. Alternatively, you can install
Keydrown via Bower:
$: bower install keydrown
Keydrown always creates the kd
browser global, but it can also be loaded as
an AMD module or as a CommonJS/Node-like module (through a tool like
Browserify).
// Loaded with an AMD loader (such as Require.js)
require(['./path/to/keydrown'], function (kd) {
kd.run(function () {
kd.tick();
});
});
// Loaded as a CommonJS module, after running it through Browserify or similar
var kd = require('./path/to/keydrown');
kd.run(function () {
kd.tick();
});
Keydrown supports all modern browsers, as well as Internet Explorer 7 and up (please see the note below about IE compatibility).
Keydrown has a feature where when the user blurs the browser window (for
example, switching to another application or tab), the key state is reset and
"down" handlers stop firing. This functionality is not supported in IE 7 and
8, as there doesn't seem to be a way to bind to the window
's blur
event
correctly in those browsers. You can assign a function to window.onblur
, but
that function will only fire once IE regains focus, which is not sufficient for
Keydrown's reset-on-blur functionality.