Look at the following JavaScript file. If you scroll down to line 530 you will find the following class:
var Keyboard = new Class(function (constructor) {
var key = {};
var eventListener = {
keyup: {},
keydown: {},
keypress: {}
};
constructor.overload(["Number"], function (interval) {
setInterval(keypress, interval);
});
window.addEventListener("keyup", keyup, false);
window.addEventListener("keydown", keydown, false);
function keyup(event) {
var keyCode = event.keyCode;
var listener = eventListener.keyup[keyCode];
key[keyCode] = false;
if (listener)
listener();
}
function keydown(event) {
var keyCode = event.keyCode;
var listener = eventListener.keydown[keyCode];
key[keyCode] = true;
if (listener)
listener();
}
function keypress() {
for (var code in key) {
var listener = eventListener.keypress[code];
if (key[code] && listener) listener();
}
}
this.addEventListener = new Dispatcher(["String", "Number", "Function"], function (type, keyCode, listener) {
type = eventListener[type];
if (type) type[keyCode] = listener;
else throw new Error("Unexpected value for type.");
});
});
What the author has done is that he has created a special Keyboard
class for delegating the key events: keyup
, keydown
and keypress
. The class has only one constructor which accepts a single argument - the interval of the keypress
event (which is what you want). You can add event listeners using the addEventListener
method of the instance of the Keyboard
class:
var keyboard = new Keyboard(125); // fire key press 8 times a second.
keypress.addEventListener("keypress", 65, function () {
// do something every time A is pressed
});
Note that the above class depends on the following framework: Lambda JS. You can see a working demo of the above script here. Hope this helps.
Update 1:
Your code does not work in Opera. In addition the second event fires after a extra 500 ms delay in Firefox and consecutive events do not maintain the same interval. Plus it can't handle multiple key events at the same time. Let's rectify this problem:
First we need to create a simple script for Delta Timing so that the key events fire after constant interval. We use the following snippet for creating a DeltaTimer
:
function DeltaTimer(render, interval) {
var timeout;
var lastTime;
this.start = start;
this.stop = stop;
function start() {
timeout = setTimeout(loop, 0);
lastTime = Date.now();
return lastTime;
}
function stop() {
clearTimeout(timeout);
return lastTime;
}
function loop() {
var thisTime = Date.now();
var deltaTime = thisTime - lastTime;
var delay = Math.max(interval - deltaTime, 0);
timeout = setTimeout(loop, delay);
lastTime = thisTime + delay;
render(thisTime);
}
}
Next we write the logic to fire custom keypressed
events. We need custom events since we must be able to handle multiple keys at the same time:
(function (interval) {
var keyboard = {};
window.addEventListener("keyup", keyup, false);
window.addEventListener("keydown", keydown, false);
function keyup(event) {
keyboard[event.keyCode].pressed = false;
}
function keydown(event) {
var keyCode = event.keyCode;
var key = keyboard[keyCode];
if (key) {
if (!key.start)
key.start = key.timer.start();
key.pressed = true;
} else {
var timer = new DeltaTimer(function (time) {
if (key.pressed) {
var event = document.createEvent("Event");
event.initEvent("keypressed", true, true);
event.time = time - key.start;
event.keyCode = keyCode;
window.dispatchEvent(event);
} else {
key.start = 0;
timer.stop();
}
}, interval);
key = keyboard[keyCode] = {
pressed: true,
timer: timer
};
key.start = timer.start();
}
}
})(1000);
The interval
is set at 1000
ms but you may change that. Finally to register an event we do:
window.addEventListener("keypressed", function (event) {
document.body.innerHTML += event.keyCode + " (" + event.time + " ms)<br/>";
}, false);
This is simple and efficient JavaScript. No jQuery required. You can see the live demo here, and see the difference between your script and mine. Cheers.
Update 2:
Looking at the other question on StackOverflow, this is how you would implement it using the above pattern:
window.addEventListener("keypressed", function (event) {
switch (event.keyCode) {
case 37:
Move(-1, 0);
break;
case 38:
Move(0, -1);
break;
case 39:
Move(1, 0);
break;
case 40:
Move(0, 1);
break;
}
}, false);
Using the above code will remove the short delay you're experiencing and also allow multiple events to be fired for different keys at the same time.
...keyboard logic
part handles various application states, where key mappings change. In hindsight, I probably should have tried harder with bobince's, before asking this question. The code in the question was my attempt at trying, though, and I didn't want to give up on it. – SpearmansetInterval
function my Delta Timer fires more accurately. Hence the 1000 ms interval you are expecting is always maintained. In addition my code fires multiple events for different keys pressed at the same time. All of them maintain a constant interval. The last time I checked it was able to handle four keys pressed at the same time. However that number is OS specific. I believe being able to handle 4 keys pressed at the same time is good enough for any application. You don't want users to spam keys. – Rajiv