3

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)

Kousha
  • 32,871
  • 51
  • 172
  • 296

4 Answers4

6

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;
        }
    });
};
})
georgeawg
  • 48,608
  • 13
  • 72
  • 95
scott
  • 583
  • 6
  • 11
2

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:

Community
  • 1
  • 1
quetzaluz
  • 1,071
  • 12
  • 17
  • I don't want a combination. The link above is if you press `a` followed by `b` for instance. I want to fire up something when `a` and `b` are pressed together at the same time. – Kousha Apr 18 '14 at 19:57
  • The last example contains a service for managing simultaneous keypresses: http://jsfiddle.net/firehist/nzUBg/ All I've been able to find, will update if I find more. Just trying to help! – quetzaluz Apr 18 '14 at 20:25
  • the last jsfiddle was very good, I used that one on my project for key combination handling! thanks! – Mario Jun 23 '16 at 12:24
  • when I create a keycombination with the JSFiddle, how to create a combination for numbpad numbers and regular numbers in combination with an alt press? I want that combination to switch accordion tabs on a web page. – Mario Jul 06 '16 at 06:48
1

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
  }
});
0

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;
    }
});
Community
  • 1
  • 1
Issam Zoli
  • 2,724
  • 1
  • 21
  • 35
  • Yes, I was thinking of that. But I want to make it a service, and make it available where-ever I inject it. I modified my question a bit – Kousha Apr 18 '14 at 19:10