Reputation: 36219
I have the following $broadcast
to catch keypresses in Angular:
$document.bind('keypress', function(event) {
var key = event.which;
$rootScope.$broadcast('keypress', event);
$rootScope.$broadcast('keypress:' + key, event);
});
and I listen with $on
However, I want to detect when two keys are pressed at the same time, say enter
and s
are pressed at the same time (not a combination one one followed by another).
What is the best way of doing this?
EDIT
What I was thinking was to have:
var keys = [];
$document.bind('keydown', function(event) {
keys[event.which] = true;
});
$document.bind('keyup', function(event) {
delete keys[event.which];
});
$document.bind('keypress', function(event) {
var key = event.which;
var keysPressed = [];
angular.forEach(keys, function(value, key) {
keysPressed += 'keypress:' + key;
});
$rootScope.$broadcast('keypress', event);
$rootScope.$broadcast(keysPressed, event);
});
So if I have multiple keypresses, then I create the correct $broadcast
. The problem, however, becomes that the order matters now (i.e., if I press a
then enter
, then the $broadcast
is keypress:58keypress:13
and if I press the other way, I get keypress:13keypress:58
)
Upvotes: 3
Views: 6899
Reputation: 583
Broadcast is used way too much in my opinion. Instead, maybe use a custom directive? This is an example of user pressing down Shift+Tab and it fires an event like so:
<input on-shift-tab="prevStep($event,'email')" />
app.directive('onShiftTab', function() {
return function(scope, element, attrs) {
var map = {9: false, 16: false};
element.on("keydown", function(event) {
if (event.which in map) {
map[event.which] = true;
if (map[9] && map[16]) {
scope.$apply(function(){
scope.$eval(attrs.onShiftTab, {'$event': event});
});
event.preventDefault();
}
}
});
element.on("keyup", function(event) {
if (event.which in map) {
map[event.keyCode] = false;
}
});
};
})
Upvotes: 6
Reputation: 127
There is a module for AngularJS that does exactly that kind of capture and is pretty simple to use: angularHotkeys
This is an example of the resulting code inside your controller:
hotkeys.add({
combo: 'return+s',
description: 'Shortcut description...',
callback: function() {
// your code here
}
});
Upvotes: 1
Reputation: 1131
The jQuery answer on this question addresses the problem pretty efficiently, here are some Angular specific approaches:
A plunker from that discussion that fires an event when up is pressed twice in a row:
The example above is able to achieve the same effect as the jQuery answer, but using only one keyup event listener rather than a keyup and keydown. Also has good use of $broadcast to trigger another $on event:
var upHitOnce = false;
$(document).keyup(function(event) {
if (event.which == 38) {
if (upHitOnce) {
$rootScope.$broadcast('DoubleUpFired');
$rootScope.$apply();
upHitOnce = false;
} else {
upHitOnce = true;
}
} else {
upHitOnce = false;
}
});
Detecting simultaneous keypresses such as ctrl+r
is a little more involved; Here's a jsfiddle with examples of how to do this in Angular:
Upvotes: 2
Reputation: 2774
For modifier keys(Ctrl, Alt, Shift) it's simpler since there are event.ctrlKey, event.altKey, event.shiftKey
. But for none modifier keys you should use something like this question Detect multiple keys on single keypress event in jQuery:
var map = {68: false, 69: false, 86: false};
$(document).keydown(function(e) {
if (e.keyCode in map) {
map[e.keyCode] = true;
if (map[68] && map[69] && map[86]) {
// FIRE EVENT
}
}
}).keyup(function(e) {
if (e.keyCode in map) {
map[e.keyCode] = false;
}
});
Upvotes: 0