I need to get clean keydown/keyup events without repetitions. When you press a key keydown event happens, when you release - keyup. Without messy repeated keydowns.
Here's code:
var keyDowns = rx.Observable.fromEvent(document, 'keydown');
var keyUps = rx.Observable.fromEvent(document, 'keyup');
var keyActions = rx.Observable.merge(keyDowns, keyUps);
keyActions.subscribe(function(e) {
console.log e
});
How to adapt it to do the job?
Check out Federico's answer below for a much more elegant and idiomatic solution.
You can avoid repeated items from an observable (as you see when a key is held) with distinctUntilChanged
which filters out values matching the last emitted value (where matching is determined by equality of emitted values or their values transformed by the keySelector
callback argument).
In your case, this could look as simple as something like:
var keyDowns = Rx.Observable.fromEvent(document, 'keydown');
var keyUps = Rx.Observable.fromEvent(document, 'keyup');
var keyActions = Rx.Observable
.merge(keyDowns, keyUps)
.distinctUntilChanged(function(e) { return e.type + (e.key || e.which); });
keyActions.subscribe(function(e) {
console.log(e.type, e.key || e.which, e.keyIdentifier);
});
but this falls short when holding multiple keys. In Chrome OS X, if I press and hold A, S, D, and then release A, S, D I get the following in the console:
keydown 65 U+0041
keydown 83 U+0053
keydown 68 U+0044
keyup 65 U+0041
keydown 68 U+0044
keyup 83 U+0053
keydown 68 U+0044
keyup 68 U+0044
Note the repetition of "keydown 68" (D) when releasing A and S. This is because keydown
events only repeat for the most-recently pressed key. Using a similar idea to the one in your comment, we can instead try a custom filter which maintains a list of which keys have been pressed:
var keyDowns = Rx.Observable.fromEvent(document, 'keydown');
var keyUps = Rx.Observable.fromEvent(document, 'keyup');
var keyActions = Rx.Observable
.merge(keyDowns, keyUps)
.filter((function() {
var keysPressed = {};
return function(e) {
var k = e.key || e.which;
if (e.type == 'keyup') {
delete keysPressed[k];
return true;
} else if (e.type == 'keydown') {
if (keysPressed[k]) {
return false;
} else {
keysPressed[k] = true;
return true;
}
}
};
})());
keyActions.subscribe(function(e) {
console.log(e.type, e.key || e.which, e.keyIdentifier);
});
Pressing and holding A, S, D, and then releasing A, S, D now gives:
keydown 65 U+0041
keydown 83 U+0053
keydown 68 U+0044
keyup 65 U+0041
keyup 83 U+0053
keyup 68 U+0044
which is similar to the approach you mentioned in the comment except that we're filtering the combined stream rather than pre-filtering such that the combined stream looks as we like it.
I'm not experienced enough with rx.js to assert that this is the best way to do this, but it certainly feels more in line with the Rx style. (Any comments appreciated.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With