Domanda

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)

È stato utile?

Soluzione

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

Altri suggerimenti

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:

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

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;
    }
});
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top